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