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 *
022 * @author Andrew Crosland Copyright (C) 2008
023 */
024public class MergMessage extends GridConnectMessage {
025
026    // Creates a new instance of GridConnectMessage
027    public MergMessage() {
028        super();
029    }
030
031    public MergMessage(CanMessage m) {
032        this();
033
034        // Standard or extended frame
035        setExtended(m.isExtended());
036
037        // Copy the header
038        MergMessage.this.setHeader(m.getHeader());
039
040        // Normal or Remote frame?
041        MergMessage.this.setRtr(m.isRtr());
042
043        // Data payload
044        for (int i = 0; i < m.getNumDataElements(); i++) {
045            MergMessage.this.setByte(m.getElement(i), i);
046        }
047        // Terminator
048        int offset = isExtended() ? 11 : 7;  // differs here from superclass
049        setElement(offset + m.getNumDataElements() * 2, ';');
050        setNumDataElements(offset + 1 + m.getNumDataElements() * 2);
051        log.debug("encoded as {}", MergMessage.this);
052    }
053
054    /**
055     * Set the header in MERG format
056     *
057     * @param header A valid CAN header value
058     */
059    @Override
060    public void setHeader(int header) {
061        int munged;
062        if (isExtended()) {
063            munged = ((header << 3) & 0xFFE00000) | 0x80000 | (header & 0x3FFFF);
064            log.debug("Extended header is {}, munged header is {}", header, munged);
065            super.setHeader(munged);
066        } else {
067            // 11 header bits are left justified
068            munged = header << 5;
069            log.debug("Standard header is {}, munged header is {}", header, munged);
070            // Can't use super() as we want to send 4 digits
071            setHexDigit((munged >> 12) & 0xF, 2);
072            setHexDigit((munged >> 8) & 0xF, 3);
073            setHexDigit((munged >> 4) & 0xF, 4);
074            setHexDigit(0, 5);
075        }
076    }
077
078    @Override
079    public void setRtr(boolean rtr) {
080        int offset = isExtended() ? 10 : 6;
081        setElement(offset, rtr ? 'R' : 'N');
082    }
083
084    /**
085     * Set a byte as two ASCII hex digits
086     * <p>
087     * Data bytes are encoded as two ASCII hex digits starting at byte 7 of the
088     * message.
089     *
090     * @param val the value to set, must be in range 0 - 255
091     * @param n   the index of the byte to be set
092     */
093    @Override
094    public void setByte(int val, int n) {
095        if ((n >= 0) && (n <= 7)) {
096            int index = n * 2 + (isExtended() ? 11 : 7);  // differs here from superclass
097            if ((val < 0) || (val > 255)) {
098                log.error("Byte value {} out of range 0-255 for MergMessage data payload", val, new IllegalArgumentException());
099                val = 0;
100            }
101            setHexDigit((val / 16) & 0xF, index++);
102            setHexDigit(val & 0xF, index);
103            return;
104        }
105        log.error("Byte Index {} out of range 0-7 for MergMessage data payload", n, new IllegalArgumentException());
106    }
107
108    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MergMessage.class);
109}