001package jmri.jmrix.can;
002
003import javax.annotation.concurrent.Immutable;
004
005import static jmri.util.StringUtil.twoHexFromInt;
006
007/**
008 * Base interface for immutable messages in a CANbus based message/reply protocol.
009 * <p>
010 * It is expected that any CAN based system will be based upon basic CAN concepts such as ID/header (standard or
011 * extended), Normal and RTR frames and a data field.
012 * <p>
013 * "header" refers to the full 11 or 29 bit header; which mode is separately set via the "extended" parameter
014 *
015 * @author Bob Jacobsen Copyright (C) 2008, 2009, 2010
016 */
017@Immutable
018public interface CanFrame {
019
020    /**
021     * Get the CAN Frame header.
022     *
023     * @return header value
024     */
025    int getHeader();
026
027    /**
028     * Get if the CAN Frame has an extended header.
029     *
030     * @return true if extended, else false
031     */
032    boolean isExtended();
033
034    /**
035     * Get if the CAN Frame is an RTR Frame.
036     *
037     * @return true if RTR, else false
038     */
039    boolean isRtr();
040
041    /**
042     * Get number of data bytes in the frame.
043     *
044     * @return 0-8
045     */
046    int getNumDataElements();
047
048    /**
049     * Get a single data byte in the frame.
050     *
051     * @param n the index, 0-7
052     * @return the data element value
053     */
054    int getElement(int n);
055
056    /**
057     * Get formatted monitor String. Includes if Frame is Extended. Header value at start. All values hex format. Only
058     * valid data elements are included.
059     *
060     * @return eg. "(1A ext) 81 EA 83 00 12"
061     */
062    default String monString() {
063        StringBuilder buf = new StringBuilder(32);
064        buf.append("(");
065        buf.append(Integer.toHexString(getHeader()));
066        buf.append(isExtended() ? " ext)" : ")");
067        appendHexElements(buf);
068        return buf.toString();
069    }
070
071    /**
072     * Get formatted toString. Does NOT include if Frame is Extended. All values hex format. Only valid data elements
073     * are included.
074     *
075     * @return eg. "[12] 81 EA 83"
076     */
077    default String getToString() {
078        StringBuilder buf = new StringBuilder(28);
079        buf.append("[");
080        buf.append(Integer.toHexString(getHeader()));
081        buf.append("]");
082        appendHexElements(buf);
083        return buf.toString();
084    }
085
086    /**
087     * Append the hex value of the data elements to a StringBuilder.
088     *
089     * @param sb to append the hex values to
090     */
091    default void appendHexElements(StringBuilder sb) {
092        for (int i = 0; i < getNumDataElements(); i++) {
093            sb.append(" ");
094            sb.append(twoHexFromInt(getElement(i)));
095        }
096    }
097
098    /**
099     * Compare 2 CanFrames for equality.
100     *
101     * @param a CanFrame to test
102     * @param b CanFrame to test
103     * @return true if RTR, Extended, Header and Data elements match, else false
104     */
105    default boolean isEqual(Object a, Object b) {
106        if (a instanceof CanFrame && b instanceof CanFrame) {
107            CanFrame aa = (CanFrame) a;
108            CanFrame bb = (CanFrame) b;
109            if (aa.isRtr() == bb.isRtr() && aa.isExtended() == bb.isExtended() && aa.getHeader() == bb.getHeader() && dataFramesEqual(aa, bb)) {
110                return true;
111            }
112        }
113        return false;
114    }
115
116    /**
117     * Compare 2 CanFrame data elements for equality.
118     *
119     * @param a CanFrame to test
120     * @param b CanFrame to test
121     * @return true if Data elements match, else false
122     */
123    default boolean dataFramesEqual(CanFrame a, CanFrame b) {
124        if (a.getNumDataElements() != b.getNumDataElements()) {
125            return false;
126        }
127        for (int i = 0; i < a.getNumDataElements(); i++) {
128            if (a.getElement(i) != b.getElement(i)) {
129                return false;
130            }
131        }
132        return true;
133    }
134
135    /**
136     * Check if the CAN Frame is extended OR RtR.
137     *
138     * @return true if either extended or RtR, else false
139     */
140    default boolean extendedOrRtr() {
141        return (isExtended() || isRtr());
142    }
143
144}