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}