001package jmri.jmrix.mrc; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004import java.text.DecimalFormat; 005import org.slf4j.Logger; 006import org.slf4j.LoggerFactory; 007 008/** 009 * Some of the message formats used in this class are Copyright MRC, Inc. and 010 * used with permission as part of the JMRI project. That permission does not 011 * extend to uses in other software products. If you wish to use this code, 012 * algorithm or these message formats outside of JMRI, please contact Mrc Inc 013 * for separate permission. 014 * 015 * @author Kevin Dickerson 2014 016 * @author Ken Cameron 2014 017 */ 018public class MrcPackets { 019 020 private static final Logger log = LoggerFactory.getLogger(MrcPackets.class); 021 public static final int THROTTLEPACKETCMD = 37; 022 static final int[] THROTTLEPACKETHEADER = new int[]{THROTTLEPACKETCMD, 0, THROTTLEPACKETCMD, 0}; 023 static final int THROTTLEPACKETLENGTH = 10; //length of packet less the header 024 025 public static final int FUNCTIONGROUP1PACKETCMD = 52; 026 static final int[] FUNCTIONGROUP1PACKETHEADER = new int[]{FUNCTIONGROUP1PACKETCMD, 0, FUNCTIONGROUP1PACKETCMD, 0}; 027 028 public static final int FUNCTIONGROUP2PACKETCMD = 68; 029 static final int[] FUNCTIONGROUP2PACKETHEADER = new int[]{FUNCTIONGROUP2PACKETCMD, 0, FUNCTIONGROUP2PACKETCMD, 0}; 030 031 public static final int FUNCTIONGROUP3PACKETCMD = 84; 032 static final int[] FUNCTIONGROUP3PACKETHEADER = new int[]{FUNCTIONGROUP3PACKETCMD, 0, FUNCTIONGROUP3PACKETCMD, 0}; 033 034 public static final int FUNCTIONGROUP4PACKETCMD = 116; 035 static final int[] FUNCTIONGROUP4PACKETHEADER = new int[]{FUNCTIONGROUP4PACKETCMD, 0, FUNCTIONGROUP4PACKETCMD, 0}; 036 037 public static final int FUNCTIONGROUP5PACKETCMD = 132; 038 static final int[] FUNCTIONGROUP5PACKETHEADER = new int[]{FUNCTIONGROUP5PACKETCMD, 0, FUNCTIONGROUP5PACKETCMD, 0}; 039 040 public static final int FUNCTIONGROUP6PACKETCMD = 164; 041 static final int[] FUNCTIONGROUP6PACKETHEADER = new int[]{FUNCTIONGROUP6PACKETCMD, 0, FUNCTIONGROUP6PACKETCMD, 0}; 042 043 static final int FUNCTIONGROUPLENGTH = 8; 044 045 public static final int ADDTOCONSISTPACKETCMD = 100; 046 static final int[] ADDTOCONSISTPACKETHEADER = new int[]{ADDTOCONSISTPACKETCMD, 0, ADDTOCONSISTPACKETCMD, 0}; 047 static final int ADDTOCONSISTPACKETLENGTH = 4; 048 049 public static final int CLEARCONSISTPACKETCMD = 98; 050 static final int[] CLEARCONSISTPACKETHEADER = new int[]{CLEARCONSISTPACKETCMD, 0, CLEARCONSISTPACKETCMD, 0}; 051 static final int CLEARCONSISTPACKETLENGTH = 4; 052 053 public static final int ROUTECONTROLPACKETCMD = 195; 054 static final int[] ROUTECONTROLPACKETHEADER = new int[]{ROUTECONTROLPACKETCMD, 0, ROUTECONTROLPACKETCMD, 0}; 055 static final int ROUTECONTROLPACKETLENGTH = 6; //Need to check. 056 057 public static final int CLEARROUTEPACKETCMD = 210; 058 static final int[] CLEARROUTEPACKETHEADER = new int[]{CLEARROUTEPACKETCMD, 0, CLEARROUTEPACKETCMD, 0}; 059 static final int CLEARROUTEPACKETLENGTH = 4; 060 061 public static final int ADDTOROUTEPACKETCMD = 211; 062 static final int[] ADDTOROUTEPACKETHEADER = new int[]{ADDTOROUTEPACKETCMD, 0, ADDTOROUTEPACKETCMD, 0}; 063 static final int ADDTOROUTEPACKETLENGTH = 6; 064 065 public static final int ACCESSORYPACKETCMD = 115; 066 static final int[] ACCESSORYPACKETHEADER = new int[]{ACCESSORYPACKETCMD, 0, ACCESSORYPACKETCMD, 0}; 067 static final int ACCESSORYPACKETLENGTH = 6; 068 069 public static final int WRITECVPOMCMD = 86; 070 static final int[] WRITECVPOMHEADER = new int[]{WRITECVPOMCMD, 0, WRITECVPOMCMD, 0}; 071 private static final int WRITECVPOMLENGTH = 12; 072 073 public static final int WRITECVPROGCMD = 36; 074 static final int[] WRITECVPROGHEADER = new int[]{WRITECVPROGCMD, 0, WRITECVPROGCMD, 0}; 075 private static final int WRITECVPROGLENGTH = 8; 076 077 public static final int READDECODERADDRESSCMD = 66; 078 static final int[] READDECODERADDRESS = new int[]{READDECODERADDRESSCMD, 0, READDECODERADDRESSCMD, 0, READDECODERADDRESSCMD, 0}; 079 080 public static final int READCVCMD = 67; 081 static final int[] READCVHEADER = new int[]{READCVCMD, 0, READCVCMD, 0}; 082 private static final int READCVLENGTH = 6; 083 084 public static final int PROGCMDSENTCODE = 51; 085 static final int[] PROGCMDSENT = new int[]{PROGCMDSENTCODE, 0, PROGCMDSENTCODE, 0}; 086 087 public static final int READCVHEADERREPLYCODE = 102; 088 static final int[] READCVHEADERREPLY = new int[]{READCVHEADERREPLYCODE, 0, READCVHEADERREPLYCODE, 0}; 089 static final int READCVPACKETLENGTH = 4; //need to double check the length of this packet 090 091 public static final int SETCLOCKRATIOCMD = 18; 092 static final int[] SETCLOCKRATIOHEADER = new int[]{SETCLOCKRATIOCMD, 0, SETCLOCKRATIOCMD, 0}; 093 private static final int SETCLOCKRATIOLENGTH = 4; 094 095 public static final int SETCLOCKTIMECMD = 19; 096 static final int[] SETCLOCKTIMEHEADER = new int[]{SETCLOCKTIMECMD, 0, SETCLOCKTIMECMD, 0}; 097 private static final int SETCLOCKTIMELENGTH = 6; 098 099 public static final int SETCLOCKAMPMCMD = 50; 100 static final int[] SETCLOCKAMPMHEADER = new int[]{SETCLOCKAMPMCMD, 0, SETCLOCKAMPMCMD, 0}; 101 private static final int SETCLOCKAMPMLENGTH = 4; 102 103 public static final int LOCOSOLECONTROLCODE = 34; 104 static final int[] LOCOSOLECONTROL = new int[]{LOCOSOLECONTROLCODE, 0, LOCOSOLECONTROLCODE, 0}; //Reply indicates that we are the sole controller of the loco 105 106 public static final int LOCODBLCONTROLCODE = 221; 107 static final int[] LOCODBLCONTROL = new int[]{LOCODBLCONTROLCODE, 0, LOCODBLCONTROLCODE, 0}; //Reply indicates that another throttle also has controll of the loco 108 109 public static final int GOODCMDRECEIVEDCODE = 85; 110 static final int[] GOODCMDRECEIVED = new int[]{GOODCMDRECEIVEDCODE, 0, GOODCMDRECEIVEDCODE, 0}; 111 112 public static final int BADCMDRECEIVEDCODE = 238; //Or unable to read from decoder 113 static final int[] BADCMDRECEIVED = new int[]{BADCMDRECEIVEDCODE, 0, BADCMDRECEIVEDCODE, 0}; 114 115 public static final int POWERONCMD = 130; 116 static final int[] POWERON = new int[]{POWERONCMD, 0, POWERONCMD, 0, POWERONCMD, 0, POWERONCMD, 0}; 117 118 public static final int POWEROFFCMD = 146; 119 static final int[] POWEROFF = new int[]{POWEROFFCMD, 0, POWEROFFCMD, 0, POWEROFFCMD, 0, POWEROFFCMD, 0}; 120 121 public static int getAddToConsistPacketLength() { 122 return ADDTOCONSISTPACKETHEADER.length + ADDTOCONSISTPACKETLENGTH; 123 } 124 125 public static int getClearConsistPacketLength() { 126 return CLEARCONSISTPACKETHEADER.length + CLEARCONSISTPACKETLENGTH; 127 } 128 129 public static int getRouteControlPacketLength() { 130 return ROUTECONTROLPACKETHEADER.length + ROUTECONTROLPACKETLENGTH; 131 } 132 133 public static int getClearRoutePacketLength() { 134 return CLEARROUTEPACKETHEADER.length + CLEARROUTEPACKETLENGTH; 135 } 136 137 public static int getAddToRoutePacketLength() { 138 return ADDTOROUTEPACKETHEADER.length + ADDTOROUTEPACKETLENGTH; 139 } 140 141 public static int getAccessoryPacketLength() { 142 return ACCESSORYPACKETHEADER.length + ACCESSORYPACKETLENGTH; 143 } 144 145 public static int getWriteCVPROGPacketLength() { 146 return WRITECVPROGHEADER.length + WRITECVPROGLENGTH; 147 } 148 149 public static int getWriteCVPOMPacketLength() { 150 return WRITECVPOMHEADER.length + WRITECVPOMLENGTH; 151 } 152 153 public static int getSetClockRatioPacketLength() { 154 return SETCLOCKRATIOHEADER.length + SETCLOCKRATIOLENGTH; 155 } 156 157 public static int getSetClockAmPmPacketLength() { 158 return SETCLOCKAMPMHEADER.length + SETCLOCKAMPMLENGTH; 159 } 160 161 public static int getFunctionPacketLength() { 162 return FUNCTIONGROUP1PACKETHEADER.length + FUNCTIONGROUPLENGTH; 163 } 164 165 public static int getReadDecoderAddressLength() { 166 return READDECODERADDRESS.length; 167 } 168 169 public static int getSetClockTimePacketLength() { 170 return SETCLOCKTIMEHEADER.length + SETCLOCKTIMELENGTH; 171 } 172 173 public static int getThrottlePacketLength() { 174 return THROTTLEPACKETHEADER.length + THROTTLEPACKETLENGTH; 175 } 176 177 public static int getReadCVPacketLength() { 178 return READCVHEADER.length + READCVLENGTH; 179 } 180 181 public static int getReadCVPacketReplyLength() { 182 return READCVHEADERREPLY.length + READCVPACKETLENGTH; 183 } 184 185 public static int getPowerOnPacketLength() { 186 return POWERON.length; 187 } 188 189 public static int getPowerOffPacketLength() { 190 return POWERON.length; 191 } 192 193 public static boolean startsWith(MrcMessage source, int[] match) { 194 if (match.length > (source.getNumDataElements())) { 195 return false; 196 } 197 for (int i = 0; i < match.length; i++) { 198 if ((source.getElement(i) & 255) != (match[i] & 255)) { 199 return false; 200 } 201 } 202 return true; 203 } 204 205 private static final DecimalFormat TWO_DIGITS = new DecimalFormat("00"); 206 private static final String TXT_ON = Bundle.getMessage("MrcPacketsFunctionOn"); // NOI18N 207 private static final String TXT_OFF = Bundle.getMessage("MrcPacketsFunctionOff"); // NOI18N 208 //Need to test toString() for POM 209 210 static public String toString(MrcMessage m) { 211 StringBuilder txt = new StringBuilder(); 212 if ((m.getNumDataElements() < 4) 213 || (m.getNumDataElements() >= 4 && m.getElement(0) != m.getElement(2) && m.getElement(1) != 0x01)) { // is && right there? 214 // byte 0 and byte 2 should always be the same except for a clock update packet. 215 if (m.getNumDataElements() < 4) { 216 txt.append(Bundle.getMessage("MrcPacketsShort")); // NOI18N 217 } else { 218 txt.append(Bundle.getMessage("MrcPacketsError")); // NOI18N 219 } 220 for (int i = 0; i < m.getNumDataElements(); i++) { 221 txt.append(" "); 222 txt.append(jmri.util.StringUtil.twoHexFromInt(m.getElement(i) & 0xFF)); 223 } 224 } else { 225 switch (m.getElement(0) & 0xFF) { 226 case SETCLOCKRATIOCMD: 227 txt.append(Bundle.getMessage("MrcPacketsSetClockRatio")).append(m.getElement(4)); // NOI18N 228 break; 229 case SETCLOCKTIMECMD: 230 txt.append(Bundle.getMessage("MrcPacketsSetClockTime")).append(m.getElement(4) // NOI18N 231 ).append( Bundle.getMessage("MrcPacketsClockTimeSep")).append(m.getElement(6)); // NOI18N 232 break; 233 case SETCLOCKAMPMCMD: 234 txt.append(Bundle.getMessage("MrcPacketsSetClockMode")); // NOI18N 235 break; 236 case MrcPackets.THROTTLEPACKETCMD: 237 if (m.getElement(4) != 0) { 238 txt.append(Bundle.getMessage("MrcPacketsLoco")).append(" ").append(Bundle.getMessage("MrcPacketsLocoLong")).append(" "); // NOI18N 239 } else { 240 txt.append(Bundle.getMessage("MrcPacketsLoco")).append(" ").append(Bundle.getMessage("MrcPacketsLocoShort")).append(" "); // NOI18N 241 } 242 txt.append(Integer.toString(m.getLocoAddress())); 243 if (m.getElement(10) == 0x02) { 244 txt.append(" ").append(Bundle.getMessage("MrcPackets128ss")); // NOI18N 245 //128 speed step 246 if ((m.getElement(8) & 0x80) == 0x80) { 247 txt.append(" ").append(Bundle.getMessage("MrcPacketsForward")); // NOI18N 248 } else { 249 txt.append(" ").append(Bundle.getMessage("MrcPacketsReverse")); // NOI18N 250 } 251 txt.append(" ").append(Bundle.getMessage("MrcPacketsSpeed")); // NOI18N 252 int speed = (m.getElement(8) & 0x7F) - 1; 253 switch (speed) { 254 case 0: 255 txt.append(" ").append(Bundle.getMessage("MrcPacketsEStop")); // NOI18N 256 break; 257 case -1: 258 txt.append(" ").append(Bundle.getMessage("MrcPacketsStop")); // NOI18N 259 break; 260 default: 261 txt.append(" ").append(Integer.toString(speed)); 262 break; 263 } 264 } else if (m.getElement(10) == 0x00) { 265 int value = m.getElement(8); 266 txt.append(" ").append(Bundle.getMessage("MrcPackets28ss")); // NOI18N 267 //28 Speed Steps 268 if ((m.getElement(8) & 0x60) == 0x60) { 269 //Forward 270 value = value - 0x60; 271 txt.append(" ").append(Bundle.getMessage("MrcPacketsForward")); // NOI18N 272 } else { 273 value = value - 0x40; 274 txt.append(" ").append(Bundle.getMessage("MrcPacketsReverse")); // NOI18N 275 } 276 if (((value >> 4) & 0x01) == 0x01) { 277 value = value - 0x10; 278 value = (value << 1) + 1; 279 } else { 280 value = value << 1; 281 } 282 value = value - 3; //Turn into user expected 0-28 283 if (value == -1) { 284 txt.append(" ").append(Bundle.getMessage("MrcPacketsEStop")); // NOI18N 285 } else { 286 if (value < 0) { 287 value = 0; 288 } 289 txt.append(" ").append(Bundle.getMessage("MrcPacketsSpeed")).append(" ").append(Integer.toString(value)); // NOI18N 290 } 291 } 292 293 break; 294 case FUNCTIONGROUP1PACKETCMD: 295 txt.append(Bundle.getMessage("MrcPacketsLoco")).append(" ").append(Integer.toString(m.getLocoAddress())) 296 .append(" ").append(Bundle.getMessage("MrcPacketsGroup1")); // NOI18N 297 txt.append(" F0 ").append(((m.getElement(8) & 0x10) != 0 ? TXT_ON : TXT_OFF)); 298 txt.append(" F1 ").append(((m.getElement(8) & 0x01) != 0 ? TXT_ON : TXT_OFF)); 299 txt.append(" F2 ").append(((m.getElement(8) & 0x02) != 0 ? TXT_ON : TXT_OFF)); 300 txt.append(" F3 ").append(((m.getElement(8) & 0x04) != 0 ? TXT_ON : TXT_OFF)); 301 txt.append(" F4 ").append(((m.getElement(8) & 0x08) != 0 ? TXT_ON : TXT_OFF)); 302 break; 303 case FUNCTIONGROUP2PACKETCMD: 304 txt.append(Bundle.getMessage("MrcPacketsLoco")).append(" ").append(Integer.toString(m.getLocoAddress())) 305 .append(" ").append(Bundle.getMessage("MrcPacketsGroup2")); // NOI18N 306 txt.append(" F5 ").append(((m.getElement(8) & 0x01) != 0 ? TXT_ON : TXT_OFF)); 307 txt.append(" F6 ").append(((m.getElement(8) & 0x02) != 0 ? TXT_ON : TXT_OFF)); 308 txt.append(" F7 ").append(((m.getElement(8) & 0x04) != 0 ? TXT_ON : TXT_OFF)); 309 txt.append(" F8 ").append(((m.getElement(8) & 0x08) != 0 ? TXT_ON : TXT_OFF)); 310 break; 311 case FUNCTIONGROUP3PACKETCMD: 312 txt.append(Bundle.getMessage("MrcPacketsLoco")).append(" ").append(Integer.toString(m.getLocoAddress())) 313 .append(" ").append(Bundle.getMessage("MrcPacketsGroup3")); // NOI18N 314 txt.append(" F9 ").append(((m.getElement(8) & 0x01) != 0 ? TXT_ON : TXT_OFF)); 315 txt.append(" F10 ").append(((m.getElement(8) & 0x02) != 0 ? TXT_ON : TXT_OFF)); 316 txt.append(" F11 ").append(((m.getElement(8) & 0x04) != 0 ? TXT_ON : TXT_OFF)); 317 txt.append(" F12 ").append(((m.getElement(8) & 0x08) != 0 ? TXT_ON : TXT_OFF)); 318 break; 319 case FUNCTIONGROUP4PACKETCMD: 320 txt.append(Bundle.getMessage("MrcPacketsLoco")).append(" ").append(Integer.toString(m.getLocoAddress())) 321 .append(" ").append(Bundle.getMessage("MrcPacketsGroup4")); // NOI18N 322 txt.append(" F13 ").append(((m.getElement(8) & 0x01) != 0 ? TXT_ON : TXT_OFF)); 323 txt.append(" F14 ").append(((m.getElement(8) & 0x02) != 0 ? TXT_ON : TXT_OFF)); 324 txt.append(" F15 ").append(((m.getElement(8) & 0x04) != 0 ? TXT_ON : TXT_OFF)); 325 txt.append(" F16 ").append(((m.getElement(8) & 0x08) != 0 ? TXT_ON : TXT_OFF)); 326 break; 327 case FUNCTIONGROUP5PACKETCMD: 328 txt.append(Bundle.getMessage("MrcPacketsLoco")).append(" ").append(Integer.toString(m.getLocoAddress())) 329 .append(" ").append(Bundle.getMessage("MrcPacketsGroup5")); // NOI18N 330 txt.append(" F17 ").append(((m.getElement(8) & 0x01) != 0 ? TXT_ON : TXT_OFF)); 331 txt.append(" F18 ").append(((m.getElement(8) & 0x02) != 0 ? TXT_ON : TXT_OFF)); 332 txt.append(" F19 ").append(((m.getElement(8) & 0x04) != 0 ? TXT_ON : TXT_OFF)); 333 txt.append(" F20 ").append(((m.getElement(8) & 0x08) != 0 ? TXT_ON : TXT_OFF)); 334 break; 335 case FUNCTIONGROUP6PACKETCMD: 336 txt.append(Bundle.getMessage("MrcPacketsLoco")).append(" ").append(Integer.toString(m.getLocoAddress())) 337 .append(" ").append(Bundle.getMessage("MrcPacketsGroup6")); // NOI18N 338 txt.append(" F21 ").append(((m.getElement(8) & 0x01) != 0 ? TXT_ON : TXT_OFF)); 339 txt.append(" F22 ").append(((m.getElement(8) & 0x02) != 0 ? TXT_ON : TXT_OFF)); 340 txt.append(" F23 ").append(((m.getElement(8) & 0x04) != 0 ? TXT_ON : TXT_OFF)); 341 txt.append(" F24 ").append(((m.getElement(8) & 0x08) != 0 ? TXT_ON : TXT_OFF)); 342 txt.append(" F25 ").append(((m.getElement(8) & 0x10) != 0 ? TXT_ON : TXT_OFF)); 343 txt.append(" F26 ").append(((m.getElement(8) & 0x20) != 0 ? TXT_ON : TXT_OFF)); 344 txt.append(" F27 ").append(((m.getElement(8) & 0x40) != 0 ? TXT_ON : TXT_OFF)); 345 txt.append(" F28 ").append(((m.getElement(8) & 0x80) != 0 ? TXT_ON : TXT_OFF)); 346 break; 347 case READCVCMD: 348 int cv = ((m.getElement(4) & 0xff) << 8) + (m.getElement(6) & 0xff); 349 txt.append(Bundle.getMessage("MrcPacketsReadCv")).append(Integer.toString(cv)); // NOI18N 350 break; 351 case READDECODERADDRESSCMD: 352 txt.append(Bundle.getMessage("MrcPacketsReadLocoAddr")); // NOI18N 353 break; 354 case WRITECVPOMCMD: 355 txt.append(Bundle.getMessage("MrcPacketsWriteOpsCvLoco")).append(" ").append(Integer.toString(m.getLocoAddress())); // NOI18N 356 txt.append(" ").append(Integer.toString((m.getElement(10) & 0xff) + 1)); 357 txt.append("="); 358 txt.append(Integer.toString(m.getElement(12) & 0xff)); 359 break; 360 case WRITECVPROGCMD: 361 txt.append(Bundle.getMessage("MrcPacketsWriteSvrCv")); // NOI18N 362 txt.append(" ").append(Integer.toString(m.getElement(4) << 8)).append(m.getElement(6)); 363 txt.append("="); 364 txt.append(Integer.toString(m.getElement(8) & 0xff)); 365 break; 366 case READCVHEADERREPLYCODE: 367 txt.append(Bundle.getMessage("MrcPacketsReadCvValue")); // NOI18N 368 txt.append(Integer.toString(m.value())); 369 break; 370 case BADCMDRECEIVEDCODE: 371 txt.append(Bundle.getMessage("MrcPacketBadCmdAck")); // NOI18N 372 break; 373 case GOODCMDRECEIVEDCODE: 374 txt.append(Bundle.getMessage("MrcPacketGoodCmdAck")); // NOI18N 375 break; 376 case PROGCMDSENTCODE: 377 txt.append(Bundle.getMessage("MrcPacketsPgmCmdSent")); // NOI18N 378 break; 379 case LOCOSOLECONTROLCODE: 380 txt.append(Bundle.getMessage("MrcPacketsSingleThrottle")); // NOI18N 381 break; 382 case LOCODBLCONTROLCODE: 383 txt.append(Bundle.getMessage("MrcPacketsMultipleThrottle")); // NOI18N 384 break; 385 case POWERONCMD: 386 txt.append(Bundle.getMessage("MrcPacketsTrkPwrOn")); // NOI18N 387 break; 388 case POWEROFFCMD: 389 txt.append(Bundle.getMessage("MrcPacketsTrkPwrOff")); // NOI18N 390 break; 391 case ADDTOCONSISTPACKETCMD: 392 txt.append(Bundle.getMessage("MrcPacketsLocoAddConsist")); // NOI18N 393 break; 394 case CLEARCONSISTPACKETCMD: 395 txt.append(Bundle.getMessage("MrcPacketsClearedConsist")); // NOI18N 396 break; 397 case ROUTECONTROLPACKETCMD: 398 txt.append(Bundle.getMessage("MrcPacketsRoute")); // NOI18N 399 txt.append(" ").append(Integer.toString(m.getElement(4))); 400 txt.append(" ").append(Bundle.getMessage("MrcPacketsRouteSet")); // NOI18N 401 break; 402 case CLEARROUTEPACKETCMD: 403 txt.append(Bundle.getMessage("MrcPacketsClearedRoute")); // NOI18N 404 break; 405 case ADDTOROUTEPACKETCMD: 406 txt.append(Bundle.getMessage("MrcPacketsAddedRoute")); // NOI18N 407 break; 408 case ACCESSORYPACKETCMD: 409 txt.append(Bundle.getMessage("MrcPacketsAccy")); // NOI18N 410 txt.append(" ").append(Integer.toString(m.getAccAddress())).append(" "); 411 switch (m.getAccState()) { 412 case jmri.Turnout.CLOSED: 413 txt.append(Bundle.getMessage("MrcPacketsAccyClosed")); // NOI18N 414 break; 415 case jmri.Turnout.THROWN: 416 txt.append(Bundle.getMessage("MrcPacketsAccyThrown")); // NOI18N 417 break; 418 default: 419 txt.append(Bundle.getMessage("MrcPacketsAccyUnk")); // NOI18N 420 } 421 break; 422 default: 423 if (m.getNumDataElements() == 6) { 424 if (m.getElement(0) == m.getElement(2) && m.getElement(0) == m.getElement(4)) { 425 txt.append(Bundle.getMessage("MrcPacketsPollToCab")).append(" "); // NOI18N 426 txt.append(m.getElement(0)); 427 } else if (m.getElement(0) == 0 && m.getElement(1) == 0x01) { 428 txt.append(Bundle.getMessage("MrcPacketsClockUpdate")).append(" "); // NOI18N 429 430 appendClockMessage(m, txt); 431 432 txt.append(" "); 433 } 434 } else if (m.getNumDataElements() == 4 && m.getElement(0) == 0x00 && m.getElement(1) == 0x00) { 435 txt.append("No Data From Last Cab"); // NOI18N 436 } else { 437 txt.append("Unk Cmd Code:"); // NOI18N 438 for (int i = 0; i < m.getNumDataElements(); i++) { 439 txt.append(" "); 440 txt.append(jmri.util.StringUtil.twoHexFromInt(m.getElement(i) & 0xFF)); 441 } 442 } 443 break; 444 } 445 } 446 return txt.toString(); 447 } 448 449 /** 450 * Adds the description of the clock's mode to a message being built 451 * 452 * @param m clock info message 453 * @param txt build description of clock info onto this 454 */ 455 @SuppressFBWarnings(value = "SF_SWITCH_NO_DEFAULT", justification = "covers all possible values") 456 static void appendClockMessage(MrcMessage m, StringBuilder txt) { 457 int clockModeBits = m.getElement(2) & 0xC0; 458 switch (clockModeBits) { 459 case 0x00: // AM format 460 txt.append((m.getElement(2) & 0x1F)) 461 .append(Bundle.getMessage("MrcPacketsClockTimeSep")) 462 .append(TWO_DIGITS.format(m.getElement(4))) 463 .append(Bundle.getMessage("MrcPacketsClockModeAm")); // NOI18N 464 break; 465 case 0x40: // PM format 466 txt.append((m.getElement(2) & 0x1F)) 467 .append(Bundle.getMessage("MrcPacketsClockTimeSep")) 468 .append(TWO_DIGITS.format(m.getElement(4))) 469 .append(Bundle.getMessage("MrcPacketsClockModePm")); // NOI18N 470 break; 471 case 0x80: // 24 hour format 472 txt.append(TWO_DIGITS.format(m.getElement(2) & 0x1F)) 473 .append(Bundle.getMessage("MrcPacketsClockTimeSep")) 474 .append(TWO_DIGITS.format(m.getElement(4))) 475 .append(Bundle.getMessage("MrcPacketsClockMode24"));// NOI18N 476 break; 477 case 0xC0: // Unk format 478 txt.append(TWO_DIGITS.format(m.getElement(2) & 0x1F)) 479 .append(Bundle.getMessage("MrcPacketsClockTimeSep")) 480 .append(TWO_DIGITS.format(m.getElement(4))) 481 .append(Bundle.getMessage("MrcPacketsClockModeUnk")); // NOI18N 482 break; 483 default: 484 log.warn("Unhandled clock mode code: {}", clockModeBits); 485 break; 486 } 487 } 488 489 //In principle last two are the checksum, the first four indicate the packet type and ignored. 490 //the rest should be XOR'd. 491 static public boolean validCheckSum(MrcMessage m) { 492 if (m.getNumDataElements() > 6) { 493 int result = 0; 494 for (int i = 4; i < m.getNumDataElements() - 2; i++) { 495 result = (m.getElement(i) & 255) ^ result; 496 } 497 if (result == (m.getElement(m.getNumDataElements() - 2) & 255)) { 498 return true; 499 } 500 } 501 return false; 502 } 503 504}