001package jmri.jmrix.can.adapters.lawicell; 002 003import jmri.jmrix.AbstractMRReply; 004import jmri.jmrix.can.CanReply; 005import org.slf4j.Logger; 006import org.slf4j.LoggerFactory; 007 008/** 009 * Class for replies in a LAWICELL message/reply protocol. 010 * <p> 011 * The Lawicell adapter protocol encodes messages as an ASCII string of up to 24 012 * characters of the form: tiiildd...[CR] Tiiiiiiiildd...[CR] The t or T 013 * indicates a standard or extended CAN frame iiiiiiii is the header as hex 014 * digits l is the number of bytes of data dd are the (up to) 8 data bytes 015 * <p> 016 * RTR Extended frames start with an R, RTR standard frames with r. 017 * @author Andrew Crosland Copyright (C) 2008 018 * @author Bob Jacobsen Copyright (C) 2008 019 * @author Steve Young Copyright (C) 2022 ( added RTR Can Frame support ) 020*/ 021public class Reply extends AbstractMRReply { 022 023 // Creates a new instance of ConnectReply 024 public Reply() { 025 super(); 026 } 027 028 public Reply(String s) { 029 _nDataChars = s.length(); 030 for (int i = 0; i < s.length(); i++) { 031 _dataChars[i] = s.charAt(i); 032 } 033 } 034 035 public CanReply createReply() { 036 // is this just an ACK to e.g. a send? 037 if (_dataChars[0] != 't' 038 && _dataChars[0] != 'T' 039 && _dataChars[0] != 'r' 040 && _dataChars[0] != 'R') { 041 log.debug("non-frame reply skipped: {}", this); 042 return null; 043 } 044 // carries a frame 045 CanReply ret = new CanReply(); 046 047 ret.setExtended(isExtended()); 048 ret.setRtr(isRtrSet()); 049 050 // Copy the header 051 ret.setHeader(getHeader()); 052 053 // Get the data 054 for (int i = 0; i < getNumBytes(); i++) { 055 ret.setElement(i, getByte(i)); 056 } 057 ret.setNumDataElements(getNumBytes()); 058 return ret; 059 } 060 061 @Override 062 protected int skipPrefix(int index) { 063 while (_dataChars[index] == ':') { 064 index++; 065 } 066 return index; 067 } 068 069 public void setData(int[] d) { 070 int len = (d.length <= 24) ? d.length : 24; 071 System.arraycopy(d, 0, _dataChars, 0, len); 072 } 073 074 public boolean isExtended() { 075 return _dataChars[0] == 'T' || _dataChars[0] == 'R'; 076 } 077 078 public boolean isRtrSet() { 079 return _dataChars[0] == 'r' || _dataChars[0] == 'R'; 080 } 081 082 /** 083 * Get the CAN header as an int 084 * 085 * @return int the CAN ID 086 */ 087 public int getHeader() { 088 if (isExtended()) { 089 // 11 bit header 090 int val = 0; 091 for (int i = 1; i <= 8; i++) { 092 val = val * 16 + getHexDigit(i); 093 } 094 return val; 095 } else { 096 // 11 bit header 097 return getHexDigit(1) * 256 098 + getHexDigit(2) * 16 + getHexDigit(3); 099 } 100 } 101 102 /** 103 * Get the number of data bytes 104 * 105 * @return int the number of bytes 106 */ 107 public int getNumBytes() { 108 if (isExtended()) { 109 return _dataChars[9] - '0'; 110 } else { 111 return _dataChars[4] - '0'; 112 } 113 } 114 115 /** 116 * Get a hex data byte from the message 117 * <p> 118 * Data bytes are encoded as two ASCII hex digits. The starting position is 119 * byte 10 or byte 5, depending on whether this is an extended or standard 120 * message 121 * 122 * @param b The byte offset (0 - 7) 123 * @return The value 124 */ 125 public int getByte(int b) { 126 if ((b >= 0) && (b <= 7)) { 127 int index = b * 2 + (isExtended() ? 10 : 5); 128 int hi = getHexDigit(index++); 129 int lo = getHexDigit(index); 130 if ((hi < 16) && (lo < 16)) { 131 return (hi * 16 + lo); 132 } 133 } 134 return 0; 135 } 136 137 // Get a single hex digit. returns 0 if digit is invalid 138 private int getHexDigit(int index) { 139 int b = _dataChars[index]; 140 if ((b >= '0') && (b <= '9')) { 141 b = b - '0'; 142 } else if ((b >= 'A') && (b <= 'F')) { 143 b = b - 'A' + 10; 144 } else if ((b >= 'a') && (b <= 'f')) { 145 b = b - 'a' + 10; 146 } else { 147 b = 0; 148 } 149 return (byte) b; 150 } 151 152 private final static Logger log = LoggerFactory.getLogger(Reply.class); 153} 154 155