001package jmri.jmrix.roco.z21; 002 003import jmri.jmrix.AbstractMRMessage; 004import org.slf4j.Logger; 005import org.slf4j.LoggerFactory; 006 007/** 008 * Class for messages in the z21/Z21 protocol. 009 * 010 * Messages have the following format: 2 bytes data length. 2 bytes op code. n 011 * bytes data. 012 * 013 * All numeric values are stored in little endian format. 014 * 015 * Carries a sequence of characters, with accessors. 016 * 017 * @author Bob Jacobsen Copyright (C) 2003 018 * @author Paul Bender Copyright (C) 2014 019 */ 020public class Z21Message extends AbstractMRMessage { 021 022 public Z21Message() { 023 super(); 024 setBinary(true); 025 } 026 027 // create a new one 028 public Z21Message(int i) { 029 this(); 030 if (i < 4) { // minimum length is 2 bytes of length, 2 bytes of opcode. 031 log.error("invalid length in call to ctor"); 032 } 033 _nDataChars = i; 034 _dataChars = new int[i]; 035 setLength(i); 036 } 037 038 // from an XpressNet message (used for protocol tunneling) 039 public Z21Message(jmri.jmrix.lenz.XNetMessage m) { 040 this(m.getNumDataElements() + 4); 041 this.setOpCode(0x0040); 042 for (int i = 0; i < m.getNumDataElements(); i++) { 043 setElement(i + 4, m.getElement(i)); 044 } 045 } 046 047 // from an LocoNetNet message (used for protocol tunneling) 048 public Z21Message(jmri.jmrix.loconet.LocoNetMessage m) { 049 this(m.getNumDataElements() + 4); 050 this.setOpCode(0x00A2); 051 for (int i = 0; i < m.getNumDataElements(); i++) { 052 setElement(i + 4, m.getElement(i)); 053 } 054 } 055 056 /** 057 * This ctor interprets the String as the exact sequence to send, 058 * byte-for-byte. 059 * 060 * @param m message string. 061 */ 062 public Z21Message(String m) { 063 super(m); 064 setBinary(true); 065 // gather bytes in result 066 byte[] b = jmri.util.StringUtil.bytesFromHexString(m); 067 if (b.length == 0) { 068 // no such thing as a zero-length message 069 _nDataChars = 0; 070 _dataChars = null; 071 return; 072 } 073 _nDataChars = b.length; 074 _dataChars = new int[_nDataChars]; 075 for (int i = 0; i < b.length; i++) { 076 setElement(i, b[i]); 077 } 078 } 079 080 /** 081 * This ctor interprets the byte array as a sequence of characters to send. 082 * 083 * @param a Array of bytes to send 084 * @param l unused. 085 */ 086 public Z21Message(byte[] a, int l) { 087 super(String.valueOf(a)); 088 setBinary(true); 089 } 090 091 @Override 092 public void setOpCode(int i) { 093 _dataChars[2] = (i & 0x00ff); 094 _dataChars[3] = ((i & 0xff00) >> 8); 095 } 096 097 @Override 098 public int getOpCode() { 099 return ( (0xff & _dataChars[2]) + ((0xff & _dataChars[3]) << 8)); 100 } 101 102 public void setLength(int i) { 103 _dataChars[0] = (i & 0x00ff); 104 _dataChars[1] = ((i & 0xff00) >> 8); 105 } 106 107 public int getLength() { 108 return (_dataChars[0] + (_dataChars[1] << 8)); 109 } 110 111 /* 112 * package protected method to get the _dataChars buffer as bytes. 113 * @return byte array containing the low order bits of the integer 114 * values in _dataChars. 115 */ 116 byte[] getBuffer() { 117 byte[] byteData = new byte[_dataChars.length]; 118 for (int i = 0; i < _dataChars.length; i++) { 119 byteData[i] = (byte) (0x00ff & _dataChars[i]); 120 } 121 return byteData; 122 } 123 124 /* 125 * canned messages 126 */ 127 128 /* 129 * @return z21 message for serial number request. 130 */ 131 public static Z21Message getSerialNumberRequestMessage() { 132 Z21Message retval = new Z21Message(4); 133 retval.setElement(0, 0x04); 134 retval.setElement(1, 0x00); 135 retval.setElement(2, 0x10); 136 retval.setElement(3, 0x00); 137 return retval; 138 } 139 140 /* 141 * @return z21 message for a hardware information request. 142 */ 143 public static Z21Message getLanGetHardwareInfoRequestMessage() { 144 Z21Message retval = new Z21Message(4); 145 retval.setElement(0, 0x04); 146 retval.setElement(1, 0x00); 147 retval.setElement(2, 0x1A); 148 retval.setElement(3, 0x00); 149 return retval; 150 } 151 152 /* 153 * @return z21 message for LAN_LOGOFF request. 154 */ 155 public static Z21Message getLanLogoffRequestMessage() { 156 Z21Message retval = new Z21Message(4){ 157 @Override 158 public boolean replyExpected() { 159 return false; // Loging off generates no reply. 160 } 161 }; 162 retval.setElement(0, 0x04); 163 retval.setElement(1, 0x00); 164 retval.setElement(2, 0x30); 165 retval.setElement(3, 0x00); 166 return retval; 167 } 168 169 /** 170 * @return z21 message for LAN_GET_BROADCAST_FLAGS request. 171 */ 172 public static Z21Message getLanGetBroadcastFlagsRequestMessage() { 173 Z21Message retval = new Z21Message(4); 174 retval.setElement(0, 0x04); 175 retval.setElement(1, 0x00); 176 retval.setElement(2, 0x51); 177 retval.setElement(3, 0x00); 178 return retval; 179 } 180 181 /** 182 * Set the broadcast flags as described in section 2.16 of the 183 * Roco Z21 Protocol Manual. 184 * <p> 185 * Brief descriptions of the flags are as follows (losely 186 * translated from German with the aid of google translate). 187 * <ul> 188 * <li>0x00000001 send XpressNet related information (track 189 * power on/off, programming mode, short circuit, broadcast stop, 190 * locomotive information, turnout information).</li> 191 * <li>0x00000002 send data changes that occur on the RMBUS.</li> 192 * <li>0x00000004 (deprecated by Roco) send Railcom Data</li> 193 * <li>0x00000100 send changes in system state (such as track voltage) 194 * <li>0x00010000 send changes to locomotives on XpressNet (must also have 195 * 0x00000001 set.</li> 196 * <li>0x01000000 forward LocoNet data to the client. Does not send 197 * Locomotive or turnout data.</li> 198 * <li>0x02000000 send Locomotive specific LocoNet data to the client.</li> 199 * <li>0x04000000 send Turnout specific LocoNet data to the client.</li> 200 * <li>0x08000000 send Occupancy information from LocoNet to the client</li> 201 * <li>0x00040000 Automatically send updates for Railcom data to the client</li> 202 * <li>0x00080000 send can detector messages to the client</li> 203 * </ul> 204 * 205 * @param flags integer representing the flags (32 bits). 206 * @return z21 message for LAN_SET_BROADCAST_FLAGS request. 207 */ 208 public static Z21Message getLanSetBroadcastFlagsRequestMessage(int flags) { 209 Z21Message retval = new Z21Message(8){ 210 @Override 211 public boolean replyExpected() { 212 return false; // setting the broadcast flags generates 213 // no reply. 214 } 215 }; 216 retval.setElement(0, 0x08); 217 retval.setElement(1, 0x00); 218 retval.setElement(2, 0x50); 219 retval.setElement(3, 0x00); 220 retval.setElement(4, (flags & 0x000000ff) ); 221 retval.setElement(5, (flags & 0x0000ff00)>>8 ); 222 retval.setElement(6, (flags & 0x00ff0000)>>16 ); 223 retval.setElement(7, (flags & 0xff000000)>>24 ); 224 return retval; 225 } 226 227 228 /** 229 * @return z21 message for LAN_RAILCOM_GETDATA request. 230 */ 231 public static Z21Message getLanRailComGetDataRequestMessage() { 232 Z21Message retval = new Z21Message(4); 233 retval.setElement(0, 0x04); 234 retval.setElement(1, 0x00); 235 retval.setElement(2, 0x89); 236 retval.setElement(3, 0x00); 237 return retval; 238 } 239 240 /** 241 * @return z21 message for LAN_SYSTEMSTATE_GETDATA 242 */ 243 public static Z21Message getLanSystemStateDataChangedRequestMessage(){ 244 Z21Message retval = new Z21Message(4); 245 retval.setElement(0, 0x04); 246 retval.setElement(1, 0x00); 247 retval.setElement(2, 0x85); 248 retval.setElement(3, 0x00); 249 return retval; 250 } 251 252 @Override 253 public String toMonitorString() { 254 switch(getOpCode()){ 255 case 0x0010: 256 return Bundle.getMessage("Z21MessageStringSerialNoRequest"); 257 case 0x001A: 258 return Bundle.getMessage("Z21MessageStringVersionRequest"); 259 case 0x0040: 260 return Bundle.getMessage("Z21MessageXpressNetTunnelRequest",new Z21XNetMessage(this).toMonitorString()); 261 case 0x0050: 262 return Bundle.getMessage("Z21MessageSetBroadcastFlags",Z21MessageUtils.interpretBroadcastFlags(_dataChars)); 263 case 0x0051: 264 return Bundle.getMessage("Z21MessageRequestBroadcastFlags"); 265 case 0x00A2: 266 return Bundle.getMessage("Z21LocoNetLanMessage", getLocoNetMessage().toMonitorString()); 267 case 0x0081: 268 return Bundle.getMessage("Z21RMBusGetDataRequest", getElement(4)); 269 case 0x0082: 270 return Bundle.getMessage("Z21RMBusProgramModuleRequest", getElement(4)); 271 case 0x0089: 272 return Bundle.getMessage("Z21_RAILCOM_GETDATA"); 273 case 0x00C4: 274 int networkID = ( getElement(4) & 0xFF) + ((getElement(5) & 0xFF) << 8); 275 return Bundle.getMessage("Z21CANDetectorRequest",networkID); 276 default: 277 } 278 return toString(); 279 } 280 281 // handle LocoNet messages tunneled in Z21 messages 282 boolean isLocoNetTunnelMessage() { 283 return( getOpCode() == 0x00A2); 284 } 285 286 boolean isLocoNetDispatchMessage() { 287 return (getOpCode() == 0x00A3); 288 } 289 290 boolean isLocoNetDetectorMessage() { 291 return (getOpCode() == 0x00A4); 292 } 293 294 jmri.jmrix.loconet.LocoNetMessage getLocoNetMessage() { 295 jmri.jmrix.loconet.LocoNetMessage lnr = null; 296 if (isLocoNetTunnelMessage()) { 297 int i = 4; 298 lnr = new jmri.jmrix.loconet.LocoNetMessage(getLength()-4); 299 for (; i < getLength(); i++) { 300 lnr.setElement(i - 4, getElement(i)); 301 } 302 } 303 return lnr; 304 } 305 306 /** 307 * @param group the RM Bus group number to request. 308 * @return z21 message for LAN_RMBUS_GETDATA 309 */ 310 public static Z21Message getLanRMBusGetDataRequestMessage(int group){ 311 if(group!=0 && group!=1){ 312 throw new IllegalArgumentException("RMBus Group not 0 or 1"); 313 } 314 Z21Message retval = new Z21Message(5); 315 retval.setElement(0, 0x04); 316 retval.setElement(1, 0x00); 317 retval.setElement(2, 0x81); 318 retval.setElement(3, 0x00); 319 retval.setElement(4, (group & 0xff)); 320 return retval; 321 } 322 323 /** 324 * @param address the RM Bus address to write. 325 * @return z21 message for LAN_RMBUS_PROGRAMMODULE 326 */ 327 public static Z21Message getLanRMBusProgramModuleMessage(int address){ 328 if(address>20){ 329 throw new IllegalArgumentException("RMBus Address > 20"); 330 } 331 Z21Message retval = new Z21Message(5); 332 retval.setElement(0, 0x05); 333 retval.setElement(1, 0x00); 334 retval.setElement(2, 0x82); 335 retval.setElement(3, 0x00); 336 retval.setElement(4, (address & 0xff)); 337 return retval; 338 } 339 340 // handle CAN Feedback/Railcom Messages 341 boolean isCanDetectorMessage() { 342 return (getOpCode() == 0x00C4); 343 } 344 345 /** 346 * @param address CAN NetworkID of the module to request data from. 347 * @return z21 message for LAN_CAN_DETECTOR request message 348 */ 349 public static Z21Message getLanCanDetector(int address){ 350 Z21Message retval = new Z21Message(7); 351 retval.setElement(0, 0x07); 352 retval.setElement(1, 0x00); 353 retval.setElement(2, 0xC4); 354 retval.setElement(3, 0x00); 355 retval.setElement(4, 0x00);// type, currently fixed. 356 retval.setElement(5, (address & 0xff)); 357 retval.setElement(6, ((address & 0xff00)>>8)); 358 return retval; 359 } 360 361 private static final Logger log = LoggerFactory.getLogger(Z21Message.class); 362 363}