001package jmri.jmrix.dcc4pc; 002 003import org.slf4j.Logger; 004import org.slf4j.LoggerFactory; 005 006/** 007 * Encodes a message to the DCC4PC Interface. 008 * <p> 009 * The {@link Dcc4PcReply} class handles the response from the command station. 010 * 011 * @author Bob Jacobsen Copyright (C) 2001 012 * @author Kevin Dickerson Copyright (C) 2012 013 * 014 */ 015public class Dcc4PcMessage extends jmri.jmrix.AbstractMRMessage { 016 017 public static final int MAXSIZE = 515; 018 019 // create a new one 020 public Dcc4PcMessage(int i) { 021 if (i < 1) { 022 log.error("invalid length in call to ctor"); 023 } 024 _nDataChars = i; 025 _dataChars = new int[i]; 026 setBinary(true); 027 setRetries(3); 028 } 029 030 /** 031 * Creates a new Dcc4PcMessage containing a byte array to represent a packet 032 * to output 033 * 034 * @param packet The contents of the packet 035 */ 036 public Dcc4PcMessage(byte[] packet) { 037 this((packet.length)); 038 int i = 0; // counter of byte in output message 039 int j; // counter of byte in input packet 040 setBinary(true); 041 // add each byte of the input message 042 for (j = 0; j < packet.length; j++) { 043 this.setElement(i, packet[i]); 044 i++; 045 } 046 setRetries(1); 047 } 048 049 // from String 050 public Dcc4PcMessage(String s) { 051 _nDataChars = s.length(); 052 _dataChars = new int[_nDataChars]; 053 for (int i = 0; i < _nDataChars; i++) { 054 _dataChars[i] = s.charAt(i); 055 } 056 setBinary(true); 057 setRetries(3); 058 } 059 060 // copy one 061 public Dcc4PcMessage(Dcc4PcMessage m) { 062 if (m == null) { 063 log.error("copy ctor of null message"); 064 return; 065 } 066 _nDataChars = m._nDataChars; 067 _dataChars = new int[_nDataChars]; 068 for (int i = 0; i < _nDataChars; i++) { 069 _dataChars[i] = m._dataChars[i]; 070 } 071 setBinary(true); 072 setRetries(1); 073 } 074 075 public void setForChildBoard(boolean boo) { 076 childBoard = boo; 077 } 078 079 boolean childBoard = false; 080 081 public boolean isForChildBoard() { 082 return childBoard; 083 } 084 085 /** {@inheritDoc} */ 086 @Override 087 public void setElement(int n, int v) { 088 _dataChars[n] = v; 089 } 090 091 /** {@inheritDoc} */ 092 @Override 093 public String toString() { 094 StringBuilder buf = new StringBuilder(); 095 buf.append("0x"); 096 buf.append(Integer.toHexString(0xFF & _dataChars[0])); 097 for (int i = 1; i < _nDataChars; i++) { 098 buf.append(".0x"); 099 buf.append(Integer.toHexString(0xff & _dataChars[i])); 100 //buf.append(", 0x" + Integer.toHexString(0xFF & _dataChars[i])); 101 } 102 return buf.toString(); 103 } 104 105 /** 106 * Get formatted message for direct output to stream - this is the final 107 * format of the message as a byte array 108 * 109 * @return the formatted message as a byte array 110 */ 111 public byte[] getFormattedMessage() { 112 int len = this.getNumDataElements(); 113 // space for carriage return if required 114 int cr = 0; 115 116 byte msg[] = new byte[len + cr]; 117 118 for (int i = 0; i < len; i++) { 119 msg[i] = (byte) this.getElement(i); 120 } 121 return msg; 122 } 123 124 //Not supported 125 public static Dcc4PcMessage getProgMode() { 126 Dcc4PcMessage m = new Dcc4PcMessage(1); 127 //m.setOpCode('+'); 128 return m; 129 } 130 131 //Not supported 132 public static Dcc4PcMessage getExitProgMode() { 133 Dcc4PcMessage m = new Dcc4PcMessage(1); 134 // m.setOpCode(' '); 135 return m; 136 } 137 138 boolean isResponse = false; 139 140 public boolean isGetResponse() { 141 return isResponse; 142 } 143 144 int board = -1; 145 146 public int getBoard(){ 147 return board; 148 } 149 150 int messageType = -1; 151 public int getMessageType(){ 152 return messageType; 153 } 154 155 static final int INFO = 0x00; 156 static final int DESC = 0x01; 157 static final int SERIAL = 0x02; 158 static final int CHILDSELECTENCODING = 0x06; 159 static final int CHILDENABLEDINPUTS = 0x07; 160 static final int CHILDRESET = 0x09; 161 static final int CHILDPOLL = 0x0a; 162 static final int RESPONSE = 0x0c; 163 164 public static Dcc4PcMessage getInfo(int address) { 165 Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) INFO}); 166 m.childBoard = true; 167 m.board = address; 168 m.messageType = INFO; 169 m.setRetries(2); 170 return m; 171 } 172 173 public static Dcc4PcMessage getDescription(int address) { 174 Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) DESC}); 175 m.childBoard = true; 176 m.board = address; 177 m.messageType = DESC; 178 return m; 179 } 180 181 public static Dcc4PcMessage getSerialNumber(int address) { 182 Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) SERIAL}); 183 m.childBoard = true; 184 m.board = address; 185 m.messageType = SERIAL; 186 return m; 187 } 188 189 static final int CHILDSETSPEED = 0x04; 190 // interface (BUS) commands; offset so they can't be mistaken for child board commands 191 static final int BUSSETSPEED = 0x107; 192 static final int BUSRESETSPEEDS = 0x10D; 193 194 /** SPEED code for 62.5 kbps (Omnibus spec section 6). */ 195 public static final int SPEED_62K5 = 4; 196 197 /** 198 * Mandatory Set Speed (0x04) for a board: it switches to the new bus speed 199 * once this transaction is complete. The interface must then be told with 200 * {@link #busSetSpeed(int, int)}. 201 * 202 * @param address board address 203 * @param speed SPEED code, 0 for 8 kbps 204 * @return the message 205 */ 206 public static Dcc4PcMessage setBoardSpeed(int address, int speed) { 207 Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) CHILDSETSPEED, (byte) speed}); 208 m.childBoard = true; 209 m.board = address; 210 m.messageType = CHILDSETSPEED; 211 return m; 212 } 213 214 /** 215 * BUS Set Speed (0x07): the speed the interface uses to talk to one board. 216 * 217 * @param address board address 218 * @param speed SPEED code, 0 for 8 kbps 219 * @return the message 220 */ 221 public static Dcc4PcMessage busSetSpeed(int address, int speed) { 222 Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x07, (byte) address, (byte) speed}); 223 m.messageType = BUSSETSPEED; 224 m.noDataReply = true; 225 return m; 226 } 227 228 // The interface answers some commands with no bytes at all, only DSR rising 229 // and falling. The receive side can't see that, so the traffic controller 230 // waits for DSR to fall instead of waiting for a reply. 231 boolean noDataReply = false; 232 233 /** 234 * @return true if the interface answers this message with no data, only 235 * its DSR handshake 236 */ 237 public boolean isNoDataReply() { 238 return noDataReply; 239 } 240 241 @Override 242 public boolean replyExpected() { 243 return !noDataReply; 244 } 245 246 /** 247 * BUS Reset Speeds (0x0D, BUS 1.1): every board and the interface go back 248 * to 8 kbps. 249 * 250 * @return the message 251 */ 252 public static Dcc4PcMessage busResetSpeeds() { 253 Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0D}); 254 m.messageType = BUSRESETSPEEDS; 255 m.noDataReply = true; 256 return m; 257 } 258 259 /** 260 * Select the Raw RailCom encoding (encoding 0), the only one decoded. 261 * 262 * @param address board address 263 * @return the message 264 */ 265 public static Dcc4PcMessage selectRawEncoding(int address) { 266 Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) CHILDSELECTENCODING, (byte) 0x00}); 267 m.childBoard = true; 268 m.board = address; 269 m.messageType = CHILDSELECTENCODING; 270 return m; 271 } 272 273 public static Dcc4PcMessage resetBoardData(int address) { 274 Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) CHILDRESET}); 275 m.childBoard = true; 276 m.board = address; 277 m.messageType = CHILDRESET; 278 return m; 279 } 280 281 public static Dcc4PcMessage pollBoard(int address){ 282 Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) CHILDPOLL}); 283 m.childBoard = true; 284 m.setTimeout(3000); // a Get Data at 8 kbps takes over 500 ms; timing out early lets the next command interrupt it 285 m.board = address; 286 m.messageType = CHILDPOLL; 287 return m; 288 } 289 290 public static Dcc4PcMessage getEnabledInputs(int address) { 291 Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) CHILDENABLEDINPUTS}); 292 m.childBoard = true; 293 m.board = address; 294 m.messageType = CHILDENABLEDINPUTS; 295 return m; 296 } 297 298 public static Dcc4PcMessage getInfo() { 299 Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) INFO}); 300 m.messageType = INFO; 301 return m; 302 } 303 304 public static Dcc4PcMessage getResponse() { 305 Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) RESPONSE}); 306 m.isResponse = true; 307 m.messageType = RESPONSE; 308 return m; 309 } 310 311 public static Dcc4PcMessage getDescription() { 312 Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) DESC}); 313 m.messageType = DESC; 314 return m; 315 } 316 317 public static Dcc4PcMessage getSerialNumber() { 318 Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) SERIAL}); 319 m.messageType = SERIAL; 320 return m; 321 } 322 323 private static final Logger log = LoggerFactory.getLogger(Dcc4PcMessage.class); 324}