001package jmri.jmrix;
002
003import java.io.Serializable;
004import org.slf4j.Logger;
005import org.slf4j.LoggerFactory;
006
007/**
008 * Represents a single general command or response.
009 * <p>
010 * Content is represented with ints to avoid the problems with sign-extension
011 * that bytes have, and because a Java char is actually a variable number of
012 * bytes in Unicode.
013 * <p>
014 * Both a set of indexed contents, an opcode, and a length field are available.
015 * Different implementations will map the opcode and length into the contents in
016 * different ways. They may not appear at all...
017 *
018 * @author Bob Jacobsen Copyright (C) 2002
019 */
020public abstract class NetMessage implements Serializable {
021
022    /**
023     * Create a new object, representing a specific-length message.
024     *
025     * @param len Total bytes in message, including opcode and error-detection
026     *            byte.
027     */
028    public NetMessage(int len) {
029        if (len < 0) {
030            log.error("invalid length in call to ctor: {}", len);
031        }
032        mNDataBytes = len;
033        mDataBytes = new int[len];
034    }
035
036    public void setOpCode(int i) {
037        mOpCode = i;
038    }
039
040    public int getOpCode() {
041        return mOpCode;
042    }
043
044    /**
045     * Get a String representation of the op code in hex.
046     * @return string of Opcode, 0x format.
047     */
048    public String getOpCodeHex() {
049        return "0x" + Integer.toHexString(getOpCode());
050    }
051
052    /**
053     * Get length, including op code and error-detection byte
054     * @return total number of data elements.
055     */
056    public int getNumDataElements() {
057        return mNDataBytes;
058    }
059
060    public int getElement(int n) {
061        if (n < 0 || n >= mDataBytes.length) {
062            log.error("illegal get element {} in message of {} elements: {}", n, mDataBytes.length, this.toString());
063        }
064        return mDataBytes[n];
065    }
066
067    public void setElement(int n, int v) {
068        if (n < 0 || n >= mDataBytes.length) {
069            log.error("illegal set element {} in message of {} elements: {}", n, mDataBytes.length, this.toString());
070        }
071        mDataBytes[n] = v;
072    }
073
074    /**
075     * Get a String representation of the entire message in hex. This is not
076     * intended to be human-readable!
077     */
078    @Override
079    public String toString() {
080        StringBuilder s = new StringBuilder();
081        for (int i = 0; i < mNDataBytes; i++) {
082            s.append(Integer.toHexString(mDataBytes[i])).append(" ");
083        }
084        return s.toString();
085    }
086
087    /**
088     * check whether the message has a valid parity
089     * @return true if parity valid, else false.
090     */
091    public abstract boolean checkParity();
092
093    /**
094     * Set parity to be correct for this implementation. Note that parity is
095     * really a stand-in for whatever error checking, etc needs to be done
096     */
097    public abstract void setParity();
098
099    // manipulate various things in a convenient way
100    static protected int lowByte(int val) {
101        return val & 0xFF;
102    }
103
104    static protected int highByte(int val) {
105        if ((val & (~0xFFFF)) != 0) {
106            log.error("highByte called with too large value: {}", Integer.toHexString(val));
107        }
108        return (val & 0xFF00) / 256;
109    }
110
111    /**
112     * Number of contained data bytes. This is not necessarily the same as the
113     * length of the data array, depending on implementation details, so is kept
114     * separate
115     */
116    private int mNDataBytes = 0;
117    private int mDataBytes[] = null;
118    private int mOpCode = 0;
119
120    // initialize logging
121    private final static Logger log = LoggerFactory.getLogger(NetMessage.class);
122
123}