001package jmri.jmrix.can.adapters.gridconnect; 002 003import jmri.jmrix.AbstractMRMessage; 004import jmri.jmrix.can.CanMessage; 005import org.slf4j.Logger; 006import org.slf4j.LoggerFactory; 007 008/** 009 * Class for GridConnect messages for a CAN hardware adapter. 010 * <p> 011 * The GridConnect protocol encodes messages as an ASCII string of up to 24 012 * characters of the form: :ShhhhNd0d1d2d3d4d5d6d7; 013 * <p> 014 * The S indicates a standard 015 * CAN frame :XhhhhhhhhNd0d1d2d3d4d5d6d7; The X indicates an extended CAN frame 016 * hhhh is the two byte header N or R indicates a normal or remote frame, in 017 * position 6 or 10 d0 - d7 are the (up to) 8 data bytes 018 * 019 * @author Andrew Crosland Copyright (C) 2008 020 */ 021public class GridConnectMessage extends AbstractMRMessage { 022 023 /** 024 * Create a new instance of GridConnectMessage. 025 */ 026 public GridConnectMessage() { 027 _nDataChars = 28; 028 _dataChars = new int[_nDataChars]; 029 GridConnectMessage.this.setElement(0, ':'); 030 } 031 032 /** 033 * Create a new GridConnectMessage from CanMessage. 034 * @param m CanMessage outgoing from JMRI. 035 */ 036 public GridConnectMessage(CanMessage m) { 037 this(); 038 addCanMessage(m); 039 } 040 041 private void addCanMessage(CanMessage m) { 042 // Standard or extended frame 043 setExtended(m.isExtended()); 044 045 // Copy the header 046 setHeader(m.getHeader()); 047 048 // Normal or Remote frame? 049 setRtr(m.isRtr()); 050 051 // Data payload 052 for (int i = 0; i < m.getNumDataElements(); i++) { 053 setByte(m.getElement(i), i); 054 } 055 // Terminator 056 int offset = isExtended() ? 11 : 6; 057 setElement(offset + m.getNumDataElements() * 2, ';'); 058 setNumDataElements(offset + 1 + m.getNumDataElements() * 2); 059 log.debug("encoded as {}", this.toString()); 060 } 061 062 /** 063 * {@inheritDoc} 064 */ 065 @Override 066 public int getNumDataElements() { 067 return _nDataChars; 068 } 069 070 /** 071 * Set Number of Data Elements. 072 * @param n number Elements. Max 28. 073 */ 074 public void setNumDataElements(int n) { 075 _nDataChars = (n <= 28) ? n : 28; 076 } 077 078 /** 079 * {@inheritDoc} 080 */ 081 @Override 082 public int getElement(int n) { 083 return _dataChars[n]; 084 } 085 086 /** 087 * {@inheritDoc} 088 */ 089 @Override 090 public void setElement(int n, int v) { 091 _dataChars[n] = v; 092 } 093 094 /** 095 * Set data from array. 096 * @param d array, max length 24. 097 */ 098 public void setData(int[] d) { 099 int len = (d.length <= 24) ? d.length : 24; 100 System.arraycopy(d, 0, _dataChars, 0, len); 101 } 102 103 /** 104 * Set the GC Message as Extended. 105 * @param extended true for extended, else false 106 */ 107 public void setExtended(boolean extended) { 108 // Standard or extended frame 109 if (extended) { 110 setElement(1, 'X'); 111 } else { 112 setElement(1, 'S'); 113 } 114 } 115 116 /** 117 * Get if the GC Message is Extended. 118 * @return true for extended, else false 119 */ 120 public boolean isExtended() { 121 return getElement(1) == 'X'; 122 } 123 124 /** 125 * Set the header. 126 * 127 * @param header A valid CAN header value. 128 */ 129 public void setHeader(int header) { 130 if (isExtended()) { 131 setHexDigit((header >> 28) & 0xF, 2); 132 setHexDigit((header >> 24) & 0xF, 3); 133 setHexDigit((header >> 20) & 0xF, 4); 134 setHexDigit((header >> 16) & 0xF, 5); 135 setHexDigit((header >> 12) & 0xF, 6); 136 setHexDigit((header >> 8) & 0xF, 7); 137 setHexDigit((header >> 4) & 0xF, 8); 138 setHexDigit(header & 0xF, 9); 139 } else { 140 setHexDigit((header >> 8) & 0xF, 2); 141 setHexDigit((header >> 4) & 0xF, 3); 142 setHexDigit(header & 0xF, 4); 143 } 144 } 145 146 /** 147 * Set CAN Frame as RtR. 148 * @param rtr true to set rtr, else false. 149 */ 150 public void setRtr(boolean rtr) { 151 int offset = isExtended() ? 10 : 5; 152 setElement(offset, rtr ? 'R' : 'N'); 153 } 154 155 /** 156 * Set a byte as two ASCII hex digits. 157 * <p> 158 * Data bytes are encoded as two ASCII hex digits starting at byte 7 of the 159 * message. 160 * 161 * @param val the value to set. 162 * @param n the index of the byte to be set. 163 */ 164 public void setByte(int val, int n) { 165 if ((n >= 0) && (n <= 7)) { 166 int index = n * 2 + (isExtended() ? 11 : 6); 167 setHexDigit((val / 16) & 0xF, index++); 168 setHexDigit(val & 0xF, index); 169 } 170 } 171 172 /** 173 * Set a hex digit at offset n in _dataChars. 174 * @param val min 0, max value 15. 175 * @param n _dataChars Array Index. 176 */ 177 protected void setHexDigit(int val, int n) { 178 if ((val >= 0) && (val <= 15)) { 179 if (val < 10) { 180 _dataChars[n] = val + '0'; 181 } else { 182 _dataChars[n] = val - 10 + 'A'; 183 } 184 } else { 185 _dataChars[n] = '0'; 186 } 187 } 188 189 private final static Logger log = LoggerFactory.getLogger(GridConnectMessage.class); 190} 191 192