001package jmri.jmrix.lenz; 002 003import java.io.Serializable; 004 005import org.slf4j.Logger; 006import org.slf4j.LoggerFactory; 007import jmri.SpeedStepMode; 008 009/** 010 * Represents a single command or response on the XpressNet. 011 * <p> 012 * Content is represented with ints to avoid the problems with sign-extension 013 * that bytes have, and because a Java char is actually a variable number of 014 * bytes in Unicode. 015 * 016 * @author Bob Jacobsen Copyright (C) 2002 017 * @author Paul Bender Copyright (C) 2003-2010 018 * 019 */ 020public class XNetMessage extends jmri.jmrix.AbstractMRMessage implements Serializable { 021 022 private static final String X_NET_MESSAGE_REQUEST_LI_BAUD = "XNetMessageRequestLIBaud"; 023 private static final String X_NET_MESSAGE_REQUEST_SERVICE_MODE_READ_DIRECT_V_36 = "XNetMessageRequestServiceModeReadDirectV36"; 024 private static final String X_NET_MESSAGE_REQUEST_SERVICE_MODE_WRITE_DIRECT_V_36 = "XNetMessageRequestServiceModeWriteDirectV36"; 025 private static final String FORWARD = "Forward"; 026 private static final String REVERSE = "Reverse"; 027 private static final String X_NET_MESSAGE_SET_SPEED = "XNetMessageSetSpeed"; 028 private static final String X_NET_MESSAGE_SET_DIRECTION = "XNetMessageSetDirection"; 029 private static final String X_NET_MESSAGE_SET_FUNCTION_GROUP_X = "XNetMessageSetFunctionGroupX"; 030 private static final String POWER_STATE_ON = "PowerStateOn"; 031 private static final String POWER_STATE_OFF = "PowerStateOff"; 032 private static final String SPEED_STEP_MODE_X = "SpeedStepModeX"; 033 private static final String X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY = "XNetMessageSetFunctionGroupXMomentary"; 034 private static final String FUNCTION_CONTINUOUS = "FunctionContinuous"; 035 private static final String FUNCTION_MOMENTARY = "FunctionMomentary"; 036 private static int _nRetries = 5; 037 038 /* According to the specification, XpressNet has a maximum timing 039 interval of 500 milliseconds during normal communications */ 040 protected static final int XNetProgrammingTimeout = 10000; 041 private static int XNetMessageTimeout = 5000; 042 043 /** 044 * Create a new object, representing a specific-length message. 045 * 046 * @param len Total bytes in message, including opcode and error-detection 047 * byte. Valid values are 0 to 15 (0x0 to 0xF). 048 */ 049 public XNetMessage(int len) { 050 super(len); 051 if (len > 15 ) { // only check upper bound. Lower bound checked in 052 // super call. 053 log.error("Invalid length in ctor: {}", len); 054 throw new IllegalArgumentException("Invalid length in ctor: " + len); 055 } 056 setBinary(true); 057 setRetries(_nRetries); 058 setTimeout(XNetMessageTimeout); 059 _nDataChars = len; 060 } 061 062 /** 063 * Create a new object, that is a copy of an existing message. 064 * 065 * @param message an existing XpressNet message 066 */ 067 public XNetMessage(XNetMessage message) { 068 super(message); 069 setBinary(true); 070 setRetries(_nRetries); 071 setTimeout(XNetMessageTimeout); 072 } 073 074 /** 075 * Create an XNetMessage from an XNetReply. 076 * @param message existing XNetReply. 077 */ 078 public XNetMessage(XNetReply message) { 079 super(message.getNumDataElements()); 080 setBinary(true); 081 setRetries(_nRetries); 082 setTimeout(XNetMessageTimeout); 083 for (int i = 0; i < message.getNumDataElements(); i++) { 084 setElement(i, message.getElement(i)); 085 } 086 } 087 088 /** 089 * Create an XNetMessage from a String containing bytes. 090 * @param s string containing data bytes. 091 */ 092 public XNetMessage(String s) { 093 setBinary(true); 094 setRetries(_nRetries); 095 setTimeout(XNetMessageTimeout); 096 // gather bytes in result 097 byte[] b = jmri.util.StringUtil.bytesFromHexString(s); 098 if (b.length == 0) { 099 // no such thing as a zero-length message 100 _nDataChars = 0; 101 _dataChars = null; 102 return; 103 } 104 _nDataChars = b.length; 105 _dataChars = new int[_nDataChars]; 106 for (int i = 0; i < b.length; i++) { 107 setElement(i, b[i]); 108 } 109 } 110 111 // note that the opcode is part of the message, so we treat it 112 // directly 113 // WARNING: use this only with opcodes that have a variable number 114 // of arguments following included. Otherwise, just use setElement 115 @Override 116 public void setOpCode(int i) { 117 if (i > 0xF || i < 0) { 118 log.error("Opcode invalid: {}", i); 119 } 120 setElement(0, ((i * 16) & 0xF0) | ((getNumDataElements() - 2) & 0xF)); 121 } 122 123 @Override 124 public int getOpCode() { 125 return (getElement(0) / 16) & 0xF; 126 } 127 128 /** 129 * Get a String representation of the op code in hex. 130 * {@inheritDoc} 131 */ 132 @Override 133 public String getOpCodeHex() { 134 return "0x" + Integer.toHexString(getOpCode()); 135 } 136 137 /** 138 * Check whether the message has a valid parity. 139 * @return true if parity valid, else false. 140 */ 141 public boolean checkParity() { 142 int len = getNumDataElements(); 143 int chksum = 0x00; /* the seed */ 144 145 int loop; 146 147 for (loop = 0; loop < len - 1; loop++) { // calculate contents for data part 148 chksum ^= getElement(loop); 149 } 150 return ((chksum & 0xFF) == getElement(len - 1)); 151 } 152 153 public void setParity() { 154 int len = getNumDataElements(); 155 int chksum = 0x00; /* the seed */ 156 157 int loop; 158 159 for (loop = 0; loop < len - 1; loop++) { // calculate contents for data part 160 chksum ^= getElement(loop); 161 } 162 setElement(len - 1, chksum & 0xFF); 163 } 164 165 /** 166 * Get an integer representation of a BCD value. 167 * @param n message element index. 168 * @return integer of BCD. 169 */ 170 public Integer getElementBCD(int n) { 171 return Integer.decode(Integer.toHexString(getElement(n))); 172 } 173 174 /** 175 * Get the message length. 176 * @return message length. 177 */ 178 public int length() { 179 return _nDataChars; 180 } 181 182 /** 183 * Set the default number of retries for an XpressNet message. 184 * 185 * @param t number of retries to attempt 186 */ 187 public static void setXNetMessageRetries(int t) { 188 _nRetries = t; 189 } 190 191 /** 192 * Set the default timeout for an XpressNet message. 193 * 194 * @param t Timeout in milliseconds 195 */ 196 public static void setXNetMessageTimeout(int t) { 197 XNetMessageTimeout = t; 198 } 199 200 /** 201 * Most messages are sent with a reply expected, but 202 * we have a few that we treat as though the reply is always 203 * a broadcast message, because the reply usually comes to us 204 * that way. 205 * {@inheritDoc} 206 */ 207 @Override 208 public boolean replyExpected() { 209 return !broadcastReply; 210 } 211 212 private boolean broadcastReply = false; 213 214 /** 215 * Tell the traffic controller we expect this 216 * message to have a broadcast reply. 217 */ 218 public void setBroadcastReply() { 219 broadcastReply = true; 220 } 221 222 // decode messages of a particular form 223 // create messages of a particular form 224 225 /** 226 * Encapsulate an NMRA DCC packet in an XpressNet message. 227 * <p> 228 * On Current (v3.5) Lenz command stations, the Operations Mode 229 * Programming Request is implemented by sending a packet directly 230 * to the rails. This packet is not checked by the XpressNet 231 * protocol, and is just the track packet with an added header 232 * byte. 233 * <p> 234 * NOTE: Lenz does not say this will work for anything but 5 235 * byte packets. 236 * @param packet byte array containing packet data elements. 237 * @return message to send DCC packet. 238 */ 239 public static XNetMessage getNMRAXNetMsg(byte[] packet) { 240 XNetMessage msg = new XNetMessage(packet.length + 2); 241 msg.setOpCode((XNetConstants.OPS_MODE_PROG_REQ & 0xF0) >> 4); 242 msg.setElement(1, 0x30); 243 for (int i = 0; i < packet.length; i++) { 244 msg.setElement((i + 2), packet[i] & 0xff); 245 } 246 msg.setParity(); 247 return (msg); 248 } 249 250 /* 251 * The next group of routines are used by Feedback and/or turnout 252 * control code. These are used in multiple places within the code, 253 * so they appear here. 254 */ 255 256 /** 257 * Generate a message to change turnout state. 258 * @param pNumber address number. 259 * @param pClose true if set turnout closed. 260 * @param pThrow true if set turnout thrown. 261 * @param pOn accessory line true for on, false off. 262 * @return message containing turnout command. 263 */ 264 public static XNetMessage getTurnoutCommandMsg(int pNumber, boolean pClose, 265 boolean pThrow, boolean pOn) { 266 XNetMessage l = new XNetMessage(4); 267 l.setElement(0, XNetConstants.ACC_OPER_REQ); 268 269 // compute address byte fields 270 int hiadr = (pNumber - 1) / 4; 271 int loadr = ((pNumber - 1) - hiadr * 4) * 2; 272 // The MSB of the upper nibble is required to be set on 273 // The rest of the upper nibble should be zeros. 274 // The MSB of the lower nibble says weather or not the 275 // accessory line should be "on" or "off" 276 if (!pOn) { 277 loadr |= 0x80; 278 } else { 279 loadr |= 0x88; 280 } 281 // If we are sending a "throw" command, we set the LSB of the 282 // lower nibble on, otherwise, we leave it "off". 283 if (pThrow) { 284 loadr |= 0x01; 285 } 286 287 // we don't know how to command both states right now! 288 if (pClose && pThrow) { 289 log.error("XpressNet turnout logic can't handle both THROWN and CLOSED yet"); 290 } 291 // store and send 292 l.setElement(1, hiadr); 293 l.setElement(2, loadr); 294 l.setParity(); // Set the parity bit 295 296 return l; 297 } 298 299 /** 300 * Generate a message to receive the feedback information for an upper or 301 * lower nibble of the feedback address in question. 302 * @param pNumber feedback address. 303 * @param pLowerNibble true for upper nibble, else false for lower. 304 * @return feedback request message. 305 */ 306 public static XNetMessage getFeedbackRequestMsg(int pNumber, 307 boolean pLowerNibble) { 308 XNetMessage l = new XNetMessage(4); 309 l.setBroadcastReply(); // we the message reply as a broadcast message. 310 l.setElement(0, XNetConstants.ACC_INFO_REQ); 311 312 // compute address byte field 313 l.setElement(1, (pNumber - 1) / 4); 314 // The MSB of the upper nibble is required to be set on 315 // The rest of the upper nibble should be zeros. 316 // The LSB of the lower nibble says weather or not the 317 // information request is for the upper or lower nibble. 318 if (pLowerNibble) { 319 l.setElement(2, 0x80); 320 } else { 321 l.setElement(2, 0x81); 322 } 323 l.setParity(); // Set the parity bit 324 return l; 325 } 326 327 /* 328 * Next, we have some messages related to sending programming commands. 329 */ 330 331 public static XNetMessage getServiceModeResultsMsg() { 332 XNetMessage m = new XNetMessage(3); 333 m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE); 334 m.setTimeout(XNetProgrammingTimeout); 335 m.setElement(0, XNetConstants.CS_REQUEST); 336 m.setElement(1, XNetConstants.SERVICE_MODE_CSRESULT); 337 m.setParity(); // Set the parity bit 338 return m; 339 } 340 341 public static XNetMessage getExitProgModeMsg() { 342 XNetMessage m = new XNetMessage(3); 343 m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE); 344 m.setElement(0, XNetConstants.CS_REQUEST); 345 m.setElement(1, XNetConstants.RESUME_OPS); 346 m.setParity(); 347 return m; 348 } 349 350 public static XNetMessage getReadPagedCVMsg(int cv) { 351 XNetMessage m = new XNetMessage(4); 352 m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE); 353 m.setTimeout(XNetProgrammingTimeout); 354 m.setElement(0, XNetConstants.PROG_READ_REQUEST); 355 m.setElement(1, XNetConstants.PROG_READ_MODE_PAGED); 356 m.setElement(2, (0xff & cv)); 357 m.setParity(); // Set the parity bit 358 return m; 359 } 360 361 public static XNetMessage getReadDirectCVMsg(int cv) { 362 XNetMessage m = new XNetMessage(4); 363 m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE); 364 m.setTimeout(XNetProgrammingTimeout); 365 m.setElement(0, XNetConstants.PROG_READ_REQUEST); 366 if (cv < 0x0100) /* Use the version 3.5 command for CVs <= 256 */ { 367 m.setElement(1, XNetConstants.PROG_READ_MODE_CV); 368 } else if (cv == 0x0400) /* For CV1024, we need to send the version 3.6 369 command for CVs 1 to 256, sending a 0 for the 370 CV */ { 371 m.setElement(1, XNetConstants.PROG_READ_MODE_CV_V36); 372 } else /* and the version 3.6 command for CVs > 256 */ { 373 m.setElement(1, XNetConstants.PROG_READ_MODE_CV_V36 | ((cv & 0x0300) >> 8)); 374 } 375 m.setElement(2, (0xff & cv)); 376 m.setParity(); // Set the parity bit 377 return m; 378 } 379 380 public static XNetMessage getWritePagedCVMsg(int cv, int val) { 381 XNetMessage m = new XNetMessage(5); 382 m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE); 383 m.setTimeout(XNetProgrammingTimeout); 384 m.setElement(0, XNetConstants.PROG_WRITE_REQUEST); 385 m.setElement(1, XNetConstants.PROG_WRITE_MODE_PAGED); 386 m.setElement(2, (0xff & cv)); 387 m.setElement(3, val); 388 m.setParity(); // Set the parity bit 389 return m; 390 } 391 392 public static XNetMessage getWriteDirectCVMsg(int cv, int val) { 393 XNetMessage m = new XNetMessage(5); 394 m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE); 395 m.setTimeout(XNetProgrammingTimeout); 396 m.setElement(0, XNetConstants.PROG_WRITE_REQUEST); 397 if (cv < 0x0100) /* Use the version 3.5 command for CVs <= 256 */ { 398 m.setElement(1, XNetConstants.PROG_WRITE_MODE_CV); 399 } else if (cv == 0x0400) /* For CV1024, we need to send the version 3.6 400 command for CVs 1 to 256, sending a 0 for the 401 CV */ { 402 m.setElement(1, XNetConstants.PROG_WRITE_MODE_CV_V36); 403 } else /* and the version 3.6 command for CVs > 256 */ { 404 m.setElement(1, XNetConstants.PROG_WRITE_MODE_CV_V36 | ((cv & 0x0300) >> 8)); 405 } 406 m.setElement(2, (0xff & cv)); 407 m.setElement(3, val); 408 m.setParity(); // Set the parity bit 409 return m; 410 } 411 412 public static XNetMessage getReadRegisterMsg(int reg) { 413 if (reg > 8) { 414 log.error("register number too large: {}",reg); 415 } 416 XNetMessage m = new XNetMessage(4); 417 m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE); 418 m.setTimeout(XNetProgrammingTimeout); 419 m.setElement(0, XNetConstants.PROG_READ_REQUEST); 420 m.setElement(1, XNetConstants.PROG_READ_MODE_REGISTER); 421 m.setElement(2, (0x0f & reg)); 422 m.setParity(); // Set the parity bit 423 return m; 424 } 425 426 public static XNetMessage getWriteRegisterMsg(int reg, int val) { 427 if (reg > 8) { 428 log.error("register number too large: {}",reg); 429 } 430 XNetMessage m = new XNetMessage(5); 431 m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE); 432 m.setTimeout(XNetProgrammingTimeout); 433 m.setElement(0, XNetConstants.PROG_WRITE_REQUEST); 434 m.setElement(1, XNetConstants.PROG_WRITE_MODE_REGISTER); 435 m.setElement(2, (0x0f & reg)); 436 m.setElement(3, val); 437 m.setParity(); // Set the parity bit 438 return m; 439 } 440 441 public static XNetMessage getWriteOpsModeCVMsg(int AH, int AL, int cv, int val) { 442 XNetMessage m = new XNetMessage(8); 443 m.setElement(0, XNetConstants.OPS_MODE_PROG_REQ); 444 m.setElement(1, XNetConstants.OPS_MODE_PROG_WRITE_REQ); 445 m.setElement(2, AH); 446 m.setElement(3, AL); 447 /* Element 4 is 0xEC + the upper two bits of the 10 bit CV address. 448 NOTE: This is the track packet CV, not the human readable CV, so 449 its value actually is one less than what we normally think of it as.*/ 450 int temp = (cv - 1) & 0x0300; 451 temp = temp / 0x00FF; 452 m.setElement(4, 0xEC + temp); 453 /* Element 5 is the lower 8 bits of the cv */ 454 m.setElement(5, ((0x00ff & cv) - 1)); 455 m.setElement(6, val); 456 m.setParity(); // Set the parity bit 457 return m; 458 } 459 460 public static XNetMessage getVerifyOpsModeCVMsg(int AH, int AL, int cv, int val) { 461 XNetMessage m = new XNetMessage(8); 462 m.setElement(0, XNetConstants.OPS_MODE_PROG_REQ); 463 m.setElement(1, XNetConstants.OPS_MODE_PROG_WRITE_REQ); 464 m.setElement(2, AH); 465 m.setElement(3, AL); 466 /* Element 4 is 0xE4 + the upper two bits of the 10 bit CV address. 467 NOTE: This is the track packet CV, not the human readable CV, so 468 its value actually is one less than what we normally think of it as.*/ 469 int temp = (cv - 1) & 0x0300; 470 temp = temp / 0x00FF; 471 m.setElement(4, 0xE4 + temp); 472 /* Element 5 is the lower 8 bits of the cv */ 473 m.setElement(5, ((0x00ff & cv) - 1)); 474 m.setElement(6, val); 475 m.setParity(); // Set the parity bit 476 return m; 477 } 478 479 public static XNetMessage getBitWriteOpsModeCVMsg(int AH, int AL, int cv, int bit, boolean value) { 480 XNetMessage m = new XNetMessage(8); 481 m.setElement(0, XNetConstants.OPS_MODE_PROG_REQ); 482 m.setElement(1, XNetConstants.OPS_MODE_PROG_WRITE_REQ); 483 m.setElement(2, AH); 484 m.setElement(3, AL); 485 /* Element 4 is 0xE8 + the upper two bits of the 10 bit CV address. 486 NOTE: This is the track packet CV, not the human readable CV, so 487 its value actually is one less than what we normally think of it as.*/ 488 int temp = (cv - 1) & 0x0300; 489 temp = temp / 0x00FF; 490 m.setElement(4, 0xE8 + temp); 491 /* Element 5 is the lower 8 bits of the cv */ 492 m.setElement(5, ((0x00ff & cv) - 1)); 493 /* Since this is a bit write, Element 6 is: 494 0xE0 + 495 bit 3 is the value to write 496 bit's 0-2 are the location of the bit we are changing */ 497 if (value) { 498 m.setElement(6, ((0xe8) | (bit & 0xff))); 499 } else // value == false 500 { 501 m.setElement(6, ((0xe0) | (bit & 0xff))); 502 } 503 m.setParity(); // Set the parity bit 504 return m; 505 } 506 507 public static XNetMessage getBitVerifyOpsModeCVMsg(int AH, int AL, int cv, int bit, boolean value) { 508 XNetMessage m = new XNetMessage(8); 509 m.setElement(0, XNetConstants.OPS_MODE_PROG_REQ); 510 m.setElement(1, XNetConstants.OPS_MODE_PROG_WRITE_REQ); 511 m.setElement(2, AH); 512 m.setElement(3, AL); 513 /* Element 4 is 0xE8 + the upper two bits of the 10 bit CV address. 514 NOTE: This is the track packet CV, not the human readable CV, so 515 its value actually is one less than what we normally think of it as.*/ 516 int temp = (cv - 1) & 0x0300; 517 temp = temp / 0x00FF; 518 m.setElement(4, 0xE8 + temp); 519 /* Element 5 is the lower 8 bits of the cv */ 520 m.setElement(5, ((0x00ff & cv) - 1)); 521 /* Since this is a bit verify, Element 6 is: 522 0xF0 + 523 bit 3 is the value to write 524 bit's 0-2 are the location of the bit we are changing */ 525 if (value) { 526 m.setElement(6, ((0xf8) | (bit & 0xff))); 527 } else // value == false 528 { 529 m.setElement(6, ((0xf0) | (bit & 0xff))); 530 } 531 m.setParity(); // Set the parity bit 532 return m; 533 } 534 535 /* 536 * Next, we have routines to generate XpressNet Messages for building 537 * and tearing down a consist or a double header. 538 */ 539 540 /** 541 * Build a Double Header. 542 * 543 * @param address1 the first address in the consist 544 * @param address2 the second address in the consist. 545 * @return message to build double header. 546 */ 547 public static XNetMessage getBuildDoubleHeaderMsg(int address1, int address2) { 548 XNetMessage msg = new XNetMessage(7); 549 msg.setElement(0, XNetConstants.LOCO_DOUBLEHEAD); 550 msg.setElement(1, XNetConstants.LOCO_DOUBLEHEAD_BYTE2); 551 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address1)); 552 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address1)); 553 msg.setElement(4, LenzCommandStation.getDCCAddressHigh(address2)); 554 msg.setElement(5, LenzCommandStation.getDCCAddressLow(address2)); 555 msg.setParity(); 556 return (msg); 557 } 558 559 /** 560 * Dissolve a Double Header. 561 * 562 * @param address one of the two addresses in the Double Header 563 * @return message to dissolve a double header. 564 */ 565 public static XNetMessage getDisolveDoubleHeaderMsg(int address) { 566 // All we have to do is call getBuildDoubleHeaderMsg with the 567 // second address as a zero 568 return (getBuildDoubleHeaderMsg(address, 0)); 569 } 570 571 /** 572 * Add a Single address to a specified Advanced consist. 573 * 574 * @param consist the consist address (1-99) 575 * @param address the locomotive address to add. 576 * @param isNormalDir tells us if the locomotive is going forward when 577 * the consist is going forward. 578 * @return message to add address to consist. 579 */ 580 public static XNetMessage getAddLocoToConsistMsg(int consist, int address, 581 boolean isNormalDir) { 582 XNetMessage msg = new XNetMessage(6); 583 msg.setElement(0, XNetConstants.LOCO_OPER_REQ); 584 if (isNormalDir) { 585 msg.setElement(1, XNetConstants.LOCO_ADD_MULTI_UNIT_REQ); 586 } else { 587 msg.setElement(1, XNetConstants.LOCO_ADD_MULTI_UNIT_REQ | 0x01); 588 } 589 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 590 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 591 msg.setElement(4, consist); 592 msg.setParity(); 593 return (msg); 594 } 595 596 /** 597 * Remove a Single address to a specified Advanced consist. 598 * 599 * @param consist the consist address (1-99) 600 * @param address the locomotive address to remove 601 * @return message to remove single address from consist. 602 */ 603 public static XNetMessage getRemoveLocoFromConsistMsg(int consist, int address) { 604 XNetMessage msg = new XNetMessage(6); 605 msg.setElement(0, XNetConstants.LOCO_OPER_REQ); 606 msg.setElement(1, XNetConstants.LOCO_REM_MULTI_UNIT_REQ); 607 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 608 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 609 msg.setElement(4, consist); 610 msg.setParity(); 611 return (msg); 612 } 613 614 615 /* 616 * Next, we have routines to generate XpressNet Messages for search 617 * and manipulation of the Command Station Database 618 */ 619 620 /** 621 * Given a locomotive address, search the database for the next 622 * member. 623 * (if the Address is zero start at the beginning of the database). 624 * 625 * @param address is the locomotive address 626 * @param searchForward indicates to search the database Forward if 627 * true, or backwards if False 628 * @return message to request next address. 629 */ 630 public static XNetMessage getNextAddressOnStackMsg(int address, boolean searchForward) { 631 XNetMessage msg = new XNetMessage(5); 632 msg.setElement(0, XNetConstants.LOCO_STATUS_REQ); 633 if (searchForward) { 634 msg.setElement(1, XNetConstants.LOCO_STACK_SEARCH_FWD); 635 } else { 636 msg.setElement(1, XNetConstants.LOCO_STACK_SEARCH_BKWD); 637 } 638 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 639 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 640 msg.setParity(); 641 return (msg); 642 } 643 644 /** 645 * Given a consist address, search the database for the next Consist 646 * address. 647 * 648 * @param address is the consist address (in the range 1-99). 649 * If the Address is zero start at the beginning of the database. 650 * @param searchForward indicates to search the database Forward if 651 * true, or backwards if false 652 * @return message to get next consist address. 653 */ 654 public static XNetMessage getDBSearchMsgConsistAddress(int address, boolean searchForward) { 655 XNetMessage msg = new XNetMessage(4); 656 msg.setElement(0, XNetConstants.CS_MULTI_UNIT_REQ); 657 if (searchForward) { 658 msg.setElement(1, XNetConstants.CS_MULTI_UNIT_REQ_FWD); 659 } else { 660 msg.setElement(1, XNetConstants.CS_MULTI_UNIT_REQ_BKWD); 661 } 662 msg.setElement(2, address); 663 msg.setParity(); 664 return (msg); 665 } 666 667 /** 668 * Given a consist and a locomotive address, search the database for 669 * the next Locomotive in the consist. 670 * 671 * @param consist the consist address (1-99). 672 * If the Consist Address is zero start at the begining of the database 673 * @param address the locomotive address. 674 * If the Address is zero start at the begining of the consist 675 * @param searchForward indicates to search the database Forward if 676 * true, or backwards if False 677 * @return message to request next loco in consist. 678 */ 679 public static XNetMessage getDBSearchMsgNextMULoco(int consist, int address, boolean searchForward) { 680 XNetMessage msg = new XNetMessage(6); 681 msg.setElement(0, XNetConstants.LOCO_IN_MULTI_UNIT_SEARCH_REQ); 682 if (searchForward) { 683 msg.setElement(1, XNetConstants.LOCO_IN_MULTI_UNIT_REQ_FORWARD); 684 } else { 685 msg.setElement(1, XNetConstants.LOCO_IN_MULTI_UNIT_REQ_BACKWARD); 686 } 687 msg.setElement(2, consist); 688 msg.setElement(3, LenzCommandStation.getDCCAddressHigh(address)); 689 msg.setElement(4, LenzCommandStation.getDCCAddressLow(address)); 690 msg.setParity(); 691 return (msg); 692 } 693 694 /** 695 * Given a locomotive address, delete it from the database . 696 * 697 * @param address the locomotive address 698 * @return message to delete loco address from stack. 699 */ 700 public static XNetMessage getDeleteAddressOnStackMsg(int address) { 701 XNetMessage msg = new XNetMessage(5); 702 msg.setElement(0, XNetConstants.LOCO_STATUS_REQ); 703 msg.setElement(1, XNetConstants.LOCO_STACK_DELETE); 704 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 705 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 706 msg.setParity(); 707 return (msg); 708 } 709 710 /** 711 * Given a locomotive address, request its status . 712 * 713 * @param address the locomotive address 714 * @return message to request loco status. 715 */ 716 public static XNetMessage getLocomotiveInfoRequestMsg(int address) { 717 XNetMessage msg = new XNetMessage(5); 718 msg.setElement(0, XNetConstants.LOCO_STATUS_REQ); 719 msg.setElement(1, XNetConstants.LOCO_INFO_REQ_V3); 720 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 721 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 722 msg.setParity(); 723 return (msg); 724 } 725 726 /** 727 * Given a locomotive address, request the function state (momentary status). 728 * 729 * @param address the locomotive address 730 * @return momentary function state request request. 731 */ 732 public static XNetMessage getLocomotiveFunctionStatusMsg(int address) { 733 XNetMessage msg = new XNetMessage(5); 734 msg.setElement(0, XNetConstants.LOCO_STATUS_REQ); 735 msg.setElement(1, XNetConstants.LOCO_INFO_REQ_FUNC); 736 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 737 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 738 msg.setParity(); 739 return (msg); 740 } 741 742 /** 743 * Given a locomotive address, request the function on/off state 744 * for functions 13-28 745 * 746 * @param address the locomotive address 747 * @return function state request request f13-f28. 748 */ 749 public static XNetMessage getLocomotiveFunctionHighOnStatusMsg(int address) { 750 XNetMessage msg = new XNetMessage(5); 751 msg.setElement(0, XNetConstants.LOCO_STATUS_REQ); 752 msg.setElement(1, XNetConstants.LOCO_INFO_REQ_FUNC_HI_ON); 753 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 754 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 755 msg.setParity(); 756 return (msg); 757 } 758 759 /** 760 * Given a locomotive address, request the function state (momentary status) 761 * for high functions (functions 13-28). 762 * 763 * @param address the locomotive address 764 * @return momentary function state request request f13-f28. 765 */ 766 public static XNetMessage getLocomotiveFunctionHighMomStatusMsg(int address) { 767 XNetMessage msg = new XNetMessage(5); 768 msg.setElement(0, XNetConstants.LOCO_STATUS_REQ); 769 msg.setElement(1, XNetConstants.LOCO_INFO_REQ_FUNC_HI_MOM); 770 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 771 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 772 msg.setParity(); 773 return (msg); 774 } 775 776 /* 777 * Generate an emergency stop for the specified address. 778 * 779 * @param address the locomotive address 780 */ 781 public static XNetMessage getAddressedEmergencyStop(int address) { 782 XNetMessage msg = new XNetMessage(4); 783 msg.setElement(0, XNetConstants.EMERGENCY_STOP); 784 msg.setElement(1, LenzCommandStation.getDCCAddressHigh(address)); 785 // set to the upper 786 // byte of the DCC address 787 msg.setElement(2, LenzCommandStation.getDCCAddressLow(address)); 788 // set to the lower byte 789 //of the DCC address 790 msg.setParity(); // Set the parity bit 791 return msg; 792 } 793 794 /** 795 * Generate a Speed and Direction Request message. 796 * 797 * @param address the locomotive address 798 * @param speedStepMode the speedstep mode see @jmri.DccThrottle 799 * for possible values. 800 * @param speed a normalized speed value (a floating point number between 0 801 * and 1). A negative value indicates emergency stop. 802 * @param isForward true for forward, false for reverse. 803 * @return set speed and direction message. 804 */ 805 public static XNetMessage getSpeedAndDirectionMsg(int address, 806 SpeedStepMode speedStepMode, 807 float speed, 808 boolean isForward) { 809 XNetMessage msg = new XNetMessage(6); 810 msg.setElement(0, XNetConstants.LOCO_OPER_REQ); 811 int element4value = 0; /* this is for holding the speed and 812 direction setting */ 813 814 if (speedStepMode == SpeedStepMode.NMRA_DCC_128) { 815 // We're in 128 speed step mode 816 msg.setElement(1, XNetConstants.LOCO_SPEED_128); 817 // Now, we need to figure out what to send in element 4 818 // Remember, the speed steps are identified as 0-127 (in 819 // 128 step mode), not 1-128. 820 int speedVal = java.lang.Math.round(speed * 126); 821 // speed step 1 is reserved to indicate emergency stop, 822 // so we need to step over speed step 1 823 if (speedVal >= 1) { 824 element4value = speedVal + 1; 825 } 826 } else if (speedStepMode == SpeedStepMode.NMRA_DCC_28) { 827 // We're in 28 speed step mode 828 msg.setElement(1, XNetConstants.LOCO_SPEED_28); 829 // Now, we need to figure out what to send in element 4 830 int speedVal = java.lang.Math.round(speed * 28); 831 // The first speed step used is actually at 4 for 28 832 // speed step mode. 833 if (speedVal >= 1) { 834 speedVal += 3; 835 } 836 // We have to re-arange the bits, since bit 4 is the LSB, 837 // but other bits are in order from 0-3 838 element4value = ((speedVal & 0x1e) >> 1) 839 + ((speedVal & 0x01) << 4); 840 } else if (speedStepMode == SpeedStepMode.NMRA_DCC_27) { 841 // We're in 27 speed step mode 842 msg.setElement(1, XNetConstants.LOCO_SPEED_27); 843 // Now, we need to figure out what to send in element 4 844 int speedVal = java.lang.Math.round(speed * 27); 845 // The first speed step used is actually at 4 for 27 846 // speed step mode. 847 if (speedVal >= 1) { 848 speedVal += 3; 849 } 850 // We have to re-arange the bits, since bit 4 is the LSB, 851 // but other bits are in order from 0-3 852 element4value = ((speedVal & 0x1e) >> 1) 853 + ((speedVal & 0x01) << 4); 854 } else { 855 // We're in 14 speed step mode 856 msg.setElement(1, XNetConstants.LOCO_SPEED_14); 857 // Now, we need to figure out what to send in element 4 858 element4value = (int) (speed * 14); 859 int speedVal = java.lang.Math.round(speed * 14); 860 // The first speed step used is actually at 2 for 14 861 // speed step mode. 862 if (speedVal >= 1) { 863 element4value += 1; 864 } 865 } 866 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 867 // set to the upper byte of the DCC address 868 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 869 // set to the lower byte 870 //of the DCC address 871 if (isForward) { 872 /* the direction bit is always the most significant bit */ 873 element4value += 128; 874 } 875 msg.setElement(4, element4value); 876 msg.setParity(); // Set the parity bit 877 return msg; 878 } 879 880 /** 881 * Generate a Function Group One Operation Request message. 882 * 883 * @param address the locomotive address 884 * @param f0 is true if f0 is on, false if f0 is off 885 * @param f1 is true if f1 is on, false if f1 is off 886 * @param f2 is true if f2 is on, false if f2 is off 887 * @param f3 is true if f3 is on, false if f3 is off 888 * @param f4 is true if f4 is on, false if f4 is off 889 * @return set function group 1 message. 890 */ 891 public static XNetMessage getFunctionGroup1OpsMsg(int address, 892 boolean f0, 893 boolean f1, 894 boolean f2, 895 boolean f3, 896 boolean f4) { 897 XNetMessage msg = new XNetMessage(6); 898 msg.setElement(0, XNetConstants.LOCO_OPER_REQ); 899 msg.setElement(1, XNetConstants.LOCO_SET_FUNC_GROUP1); 900 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 901 // set to the upper byte of the DCC address 902 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 903 // set to the lower byte of the DCC address 904 // Now, we need to figure out what to send in element 3 905 int element4value = 0; 906 if (f0) { 907 element4value += 16; 908 } 909 if (f1) { 910 element4value += 1; 911 } 912 if (f2) { 913 element4value += 2; 914 } 915 if (f3) { 916 element4value += 4; 917 } 918 if (f4) { 919 element4value += 8; 920 } 921 msg.setElement(4, element4value); 922 msg.setParity(); // Set the parity bit 923 return msg; 924 } 925 926 /** 927 * Generate a Function Group One Set Momentary Functions message. 928 * 929 * @param address the locomotive address 930 * @param f0 is true if f0 is momentary 931 * @param f1 is true if f1 is momentary 932 * @param f2 is true if f2 is momentary 933 * @param f3 is true if f3 is momentary 934 * @param f4 is true if f4 is momentary 935 * @return set momentary function group 1 message. 936 */ 937 public static XNetMessage getFunctionGroup1SetMomMsg(int address, 938 boolean f0, 939 boolean f1, 940 boolean f2, 941 boolean f3, 942 boolean f4) { 943 XNetMessage msg = new XNetMessage(6); 944 msg.setElement(0, XNetConstants.LOCO_OPER_REQ); 945 msg.setElement(1, XNetConstants.LOCO_SET_FUNC_Group1); 946 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 947 // set to the upper byte of the DCC address 948 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 949 // set to the lower byte of the DCC address 950 // Now, we need to figure out what to send in element 3 951 int element4value = 0; 952 if (f0) { 953 element4value += 16; 954 } 955 if (f1) { 956 element4value += 1; 957 } 958 if (f2) { 959 element4value += 2; 960 } 961 if (f3) { 962 element4value += 4; 963 } 964 if (f4) { 965 element4value += 8; 966 } 967 msg.setElement(4, element4value); 968 msg.setParity(); // Set the parity bit 969 return msg; 970 } 971 972 /** 973 * Generate a Function Group Two Operation Request message. 974 * 975 * @param address the locomotive address 976 * @param f5 is true if f5 is on, false if f5 is off 977 * @param f6 is true if f6 is on, false if f6 is off 978 * @param f7 is true if f7 is on, false if f7 is off 979 * @param f8 is true if f8 is on, false if f8 is off 980 * @return set function group 2 message. 981 */ 982 public static XNetMessage getFunctionGroup2OpsMsg(int address, 983 boolean f5, 984 boolean f6, 985 boolean f7, 986 boolean f8) { 987 XNetMessage msg = new XNetMessage(6); 988 msg.setElement(0, XNetConstants.LOCO_OPER_REQ); 989 msg.setElement(1, XNetConstants.LOCO_SET_FUNC_GROUP2); 990 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 991 // set to the upper byte of the DCC address 992 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 993 // set to the lower byte of the DCC address 994 // Now, we need to figure out what to send in element 3 995 int element4value = 0; 996 if (f5) { 997 element4value += 1; 998 } 999 if (f6) { 1000 element4value += 2; 1001 } 1002 if (f7) { 1003 element4value += 4; 1004 } 1005 if (f8) { 1006 element4value += 8; 1007 } 1008 msg.setElement(4, element4value); 1009 msg.setParity(); // Set the parity bit 1010 return msg; 1011 } 1012 1013 /** 1014 * Generate a Function Group Two Set Momentary Functions message. 1015 * 1016 * @param address the locomotive address 1017 * @param f5 is true if f5 is momentary 1018 * @param f6 is true if f6 is momentary 1019 * @param f7 is true if f7 is momentary 1020 * @param f8 is true if f8 is momentary 1021 * @return set momentary function group 2 message. 1022 */ 1023 public static XNetMessage getFunctionGroup2SetMomMsg(int address, 1024 boolean f5, 1025 boolean f6, 1026 boolean f7, 1027 boolean f8) { 1028 XNetMessage msg = new XNetMessage(6); 1029 msg.setElement(0, XNetConstants.LOCO_OPER_REQ); 1030 msg.setElement(1, XNetConstants.LOCO_SET_FUNC_Group2); 1031 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 1032 // set to the upper byte of the DCC address 1033 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 1034 // set to the lower byte of the DCC address 1035 // Now, we need to figure out what to send in element 3 1036 int element4value = 0; 1037 if (f5) { 1038 element4value += 1; 1039 } 1040 if (f6) { 1041 element4value += 2; 1042 } 1043 if (f7) { 1044 element4value += 4; 1045 } 1046 if (f8) { 1047 element4value += 8; 1048 } 1049 msg.setElement(4, element4value); 1050 msg.setParity(); // Set the parity bit 1051 return msg; 1052 } 1053 1054 /** 1055 * Generate a Function Group Three Operation Request message. 1056 * 1057 * @param address the locomotive address 1058 * @param f9 is true if f9 is on, false if f9 is off 1059 * @param f10 is true if f10 is on, false if f10 is off 1060 * @param f11 is true if f11 is on, false if f11 is off 1061 * @param f12 is true if f12 is on, false if f12 is off 1062 * @return set function group 3 message. 1063 */ 1064 public static XNetMessage getFunctionGroup3OpsMsg(int address, 1065 boolean f9, 1066 boolean f10, 1067 boolean f11, 1068 boolean f12) { 1069 XNetMessage msg = new XNetMessage(6); 1070 msg.setElement(0, XNetConstants.LOCO_OPER_REQ); 1071 msg.setElement(1, XNetConstants.LOCO_SET_FUNC_GROUP3); 1072 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 1073 // set to the upper byte of the DCC address 1074 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 1075 // set to the lower byte of the DCC address 1076 // Now, we need to figure out what to send in element 3 1077 int element4value = 0; 1078 if (f9) { 1079 element4value += 1; 1080 } 1081 if (f10) { 1082 element4value += 2; 1083 } 1084 if (f11) { 1085 element4value += 4; 1086 } 1087 if (f12) { 1088 element4value += 8; 1089 } 1090 msg.setElement(4, element4value); 1091 msg.setParity(); // Set the parity bit 1092 return msg; 1093 } 1094 1095 /** 1096 * Generate a Function Group Three Set Momentary Functions message. 1097 * 1098 * @param address the locomotive address 1099 * @param f9 is true if f9 is momentary 1100 * @param f10 is true if f10 is momentary 1101 * @param f11 is true if f11 is momentary 1102 * @param f12 is true if f12 is momentary 1103 * @return set momentary function group 3 message. 1104 */ 1105 public static XNetMessage getFunctionGroup3SetMomMsg(int address, 1106 boolean f9, 1107 boolean f10, 1108 boolean f11, 1109 boolean f12) { 1110 XNetMessage msg = new XNetMessage(6); 1111 msg.setElement(0, XNetConstants.LOCO_OPER_REQ); 1112 msg.setElement(1, XNetConstants.LOCO_SET_FUNC_Group3); 1113 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 1114 // set to the upper byte of the DCC address 1115 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 1116 // set to the lower byte of the DCC address 1117 // Now, we need to figure out what to send in element 3 1118 int element4value = 0; 1119 if (f9) { 1120 element4value += 1; 1121 } 1122 if (f10) { 1123 element4value += 2; 1124 } 1125 if (f11) { 1126 element4value += 4; 1127 } 1128 if (f12) { 1129 element4value += 8; 1130 } 1131 msg.setElement(4, element4value); 1132 msg.setParity(); // Set the parity bit 1133 return msg; 1134 } 1135 1136 /** 1137 * Generate a Function Group Four Operation Request message. 1138 * 1139 * @param address the locomotive address 1140 * @param f13 is true if f13 is on, false if f13 is off 1141 * @param f14 is true if f14 is on, false if f14 is off 1142 * @param f15 is true if f15 is on, false if f15 is off 1143 * @param f16 is true if f18 is on, false if f16 is off 1144 * @param f17 is true if f17 is on, false if f17 is off 1145 * @param f18 is true if f18 is on, false if f18 is off 1146 * @param f19 is true if f19 is on, false if f19 is off 1147 * @param f20 is true if f20 is on, false if f20 is off 1148 * @return set function group 4 message. 1149 */ 1150 public static XNetMessage getFunctionGroup4OpsMsg(int address, 1151 boolean f13, 1152 boolean f14, 1153 boolean f15, 1154 boolean f16, 1155 boolean f17, 1156 boolean f18, 1157 boolean f19, 1158 boolean f20) { 1159 XNetMessage msg = new XNetMessage(6); 1160 msg.setElement(0, XNetConstants.LOCO_OPER_REQ); 1161 msg.setElement(1, XNetConstants.LOCO_SET_FUNC_GROUP4); 1162 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 1163 // set to the upper byte of the DCC address 1164 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 1165 // set to the lower byte of the DCC address 1166 // Now, we need to figure out what to send in element 3 1167 int element4value = 0; 1168 if (f13) { 1169 element4value += 1; 1170 } 1171 if (f14) { 1172 element4value += 2; 1173 } 1174 if (f15) { 1175 element4value += 4; 1176 } 1177 if (f16) { 1178 element4value += 8; 1179 } 1180 if (f17) { 1181 element4value += 16; 1182 } 1183 if (f18) { 1184 element4value += 32; 1185 } 1186 if (f19) { 1187 element4value += 64; 1188 } 1189 if (f20) { 1190 element4value += 128; 1191 } 1192 msg.setElement(4, element4value); 1193 msg.setParity(); // Set the parity bit 1194 return msg; 1195 } 1196 1197 /** 1198 * Generate a Function Group Four Set Momentary Function message. 1199 * 1200 * @param address the locomotive address 1201 * @param f13 is true if f13 is Momentary 1202 * @param f14 is true if f14 is Momentary 1203 * @param f15 is true if f15 is Momentary 1204 * @param f16 is true if f18 is Momentary 1205 * @param f17 is true if f17 is Momentary 1206 * @param f18 is true if f18 is Momentary 1207 * @param f19 is true if f19 is Momentary 1208 * @param f20 is true if f20 is Momentary 1209 * @return set momentary function group 4 message. 1210 */ 1211 public static XNetMessage getFunctionGroup4SetMomMsg(int address, 1212 boolean f13, 1213 boolean f14, 1214 boolean f15, 1215 boolean f16, 1216 boolean f17, 1217 boolean f18, 1218 boolean f19, 1219 boolean f20) { 1220 XNetMessage msg = new XNetMessage(6); 1221 msg.setElement(0, XNetConstants.LOCO_OPER_REQ); 1222 msg.setElement(1, XNetConstants.LOCO_SET_FUNC_Group4); 1223 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 1224 // set to the upper byte of the DCC address 1225 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 1226 // set to the lower byte of the DCC address 1227 // Now, we need to figure out what to send in element 3 1228 int element4value = 0; 1229 if (f13) { 1230 element4value += 1; 1231 } 1232 if (f14) { 1233 element4value += 2; 1234 } 1235 if (f15) { 1236 element4value += 4; 1237 } 1238 if (f16) { 1239 element4value += 8; 1240 } 1241 if (f17) { 1242 element4value += 16; 1243 } 1244 if (f18) { 1245 element4value += 32; 1246 } 1247 if (f19) { 1248 element4value += 64; 1249 } 1250 if (f20) { 1251 element4value += 128; 1252 } 1253 msg.setElement(4, element4value); 1254 msg.setParity(); // Set the parity bit 1255 return msg; 1256 } 1257 1258 /** 1259 * Generate a Function Group Five Operation Request message. 1260 * 1261 * @param address the locomotive address 1262 * @param f21 is true if f21 is on, false if f21 is off 1263 * @param f22 is true if f22 is on, false if f22 is off 1264 * @param f23 is true if f23 is on, false if f23 is off 1265 * @param f24 is true if f24 is on, false if f24 is off 1266 * @param f25 is true if f25 is on, false if f25 is off 1267 * @param f26 is true if f26 is on, false if f26 is off 1268 * @param f27 is true if f27 is on, false if f27 is off 1269 * @param f28 is true if f28 is on, false if f28 is off 1270 * @return set function group 5 message. 1271 */ 1272 public static XNetMessage getFunctionGroup5OpsMsg(int address, 1273 boolean f21, 1274 boolean f22, 1275 boolean f23, 1276 boolean f24, 1277 boolean f25, 1278 boolean f26, 1279 boolean f27, 1280 boolean f28) { 1281 XNetMessage msg = new XNetMessage(6); 1282 msg.setElement(0, XNetConstants.LOCO_OPER_REQ); 1283 msg.setElement(1, XNetConstants.LOCO_SET_FUNC_GROUP5); 1284 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 1285 // set to the upper byte of the DCC address 1286 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 1287 // set to the lower byte of the DCC address 1288 // Now, we need to figure out what to send in element 3 1289 int element4value = 0; 1290 if (f21) { 1291 element4value += 1; 1292 } 1293 if (f22) { 1294 element4value += 2; 1295 } 1296 if (f23) { 1297 element4value += 4; 1298 } 1299 if (f24) { 1300 element4value += 8; 1301 } 1302 if (f25) { 1303 element4value += 16; 1304 } 1305 if (f26) { 1306 element4value += 32; 1307 } 1308 if (f27) { 1309 element4value += 64; 1310 } 1311 if (f28) { 1312 element4value += 128; 1313 } 1314 msg.setElement(4, element4value); 1315 msg.setParity(); // Set the parity bit 1316 return msg; 1317 } 1318 1319 /** 1320 * Generate a Function Group Five Set Momentary Function message. 1321 * 1322 * @param address the locomotive address 1323 * @param f21 is true if f21 is momentary 1324 * @param f22 is true if f22 is momentary 1325 * @param f23 is true if f23 is momentary 1326 * @param f24 is true if f24 is momentary 1327 * @param f25 is true if f25 is momentary 1328 * @param f26 is true if f26 is momentary 1329 * @param f27 is true if f27 is momentary 1330 * @param f28 is true if f28 is momentary 1331 * @return set momentary function group 5 message. 1332 */ 1333 public static XNetMessage getFunctionGroup5SetMomMsg(int address, 1334 boolean f21, 1335 boolean f22, 1336 boolean f23, 1337 boolean f24, 1338 boolean f25, 1339 boolean f26, 1340 boolean f27, 1341 boolean f28) { 1342 XNetMessage msg = new XNetMessage(6); 1343 msg.setElement(0, XNetConstants.LOCO_OPER_REQ); 1344 msg.setElement(1, XNetConstants.LOCO_SET_FUNC_Group5); 1345 msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address)); 1346 // set to the upper byte of the DCC address 1347 msg.setElement(3, LenzCommandStation.getDCCAddressLow(address)); 1348 // set to the lower byte of the DCC address 1349 // Now, we need to figure out what to send in element 3 1350 int element4value = 0; 1351 if (f21) { 1352 element4value += 1; 1353 } 1354 if (f22) { 1355 element4value += 2; 1356 } 1357 if (f23) { 1358 element4value += 4; 1359 } 1360 if (f24) { 1361 element4value += 8; 1362 } 1363 if (f25) { 1364 element4value += 16; 1365 } 1366 if (f26) { 1367 element4value += 32; 1368 } 1369 if (f27) { 1370 element4value += 64; 1371 } 1372 if (f28) { 1373 element4value += 128; 1374 } 1375 msg.setElement(4, element4value); 1376 msg.setParity(); // Set the parity bit 1377 return msg; 1378 } 1379 1380 /** 1381 * Build a Resume operations Message. 1382 * @return resume message. 1383 */ 1384 public static XNetMessage getResumeOperationsMsg() { 1385 XNetMessage msg = new XNetMessage(3); 1386 msg.setElement(0, XNetConstants.CS_REQUEST); 1387 msg.setElement(1, XNetConstants.RESUME_OPS); 1388 msg.setParity(); 1389 return (msg); 1390 } 1391 1392 /** 1393 * Build an EmergencyOff Message. 1394 * @return emergency off message. 1395 */ 1396 public static XNetMessage getEmergencyOffMsg() { 1397 XNetMessage msg = new XNetMessage(3); 1398 msg.setElement(0, XNetConstants.CS_REQUEST); 1399 msg.setElement(1, XNetConstants.EMERGENCY_OFF); 1400 msg.setParity(); 1401 return (msg); 1402 } 1403 1404 /** 1405 * Build an EmergencyStop Message. 1406 * @return emergency stop message. 1407 */ 1408 public static XNetMessage getEmergencyStopMsg() { 1409 XNetMessage msg = new XNetMessage(2); 1410 msg.setElement(0, XNetConstants.ALL_ESTOP); 1411 msg.setParity(); 1412 return (msg); 1413 } 1414 1415 /** 1416 * Generate the message to request the Command Station Hardware/Software 1417 * Version. 1418 * @return message to request CS hardware and software version. 1419 */ 1420 public static XNetMessage getCSVersionRequestMessage() { 1421 XNetMessage msg = new XNetMessage(3); 1422 msg.setElement(0, XNetConstants.CS_REQUEST); 1423 msg.setElement(1, XNetConstants.CS_VERSION); 1424 msg.setParity(); // Set the parity bit 1425 return msg; 1426 } 1427 1428 /** 1429 * Generate the message to request the Command Station Status. 1430 * @return message to request CS status. 1431 */ 1432 public static XNetMessage getCSStatusRequestMessage() { 1433 XNetMessage msg = new XNetMessage(3); 1434 msg.setElement(0, XNetConstants.CS_REQUEST); 1435 msg.setElement(1, XNetConstants.CS_STATUS); 1436 msg.setParity(); // Set the parity bit 1437 return msg; 1438 } 1439 1440 /** 1441 * Generate the message to set the Command Station to Auto or Manual restart 1442 * mode. 1443 * @param autoMode true if auto, false for manual. 1444 * @return message to set CS restart mode. 1445 */ 1446 public static XNetMessage getCSAutoStartMessage(boolean autoMode) { 1447 XNetMessage msg = new XNetMessage(4); 1448 msg.setElement(0, XNetConstants.CS_SET_POWERMODE); 1449 msg.setElement(1, XNetConstants.CS_SET_POWERMODE); 1450 if (autoMode) { 1451 msg.setElement(2, XNetConstants.CS_POWERMODE_AUTO); 1452 } else { 1453 msg.setElement(2, XNetConstants.CS_POWERMODE_MANUAL); 1454 } 1455 msg.setParity(); // Set the parity bit 1456 return msg; 1457 } 1458 1459 /** 1460 * Generate the message to request the Computer Interface Hardware/Software 1461 * Version. 1462 * @return message to request interface hardware and software version. 1463 */ 1464 public static XNetMessage getLIVersionRequestMessage() { 1465 XNetMessage msg = new XNetMessage(2); 1466 msg.setElement(0, XNetConstants.LI_VERSION_REQUEST); 1467 msg.setParity(); // Set the parity bit 1468 return msg; 1469 } 1470 1471 /** 1472 * Generate the message to set or request the Computer Interface Address. 1473 * 1474 * @param address Interface address (0-31). Send invalid address to request 1475 * the address (32-255). 1476 * @return message to set or request interface address. 1477 */ 1478 public static XNetMessage getLIAddressRequestMsg(int address) { 1479 XNetMessage msg = new XNetMessage(4); 1480 msg.setElement(0, XNetConstants.LI101_REQUEST); 1481 msg.setElement(1, XNetConstants.LI101_REQUEST_ADDRESS); 1482 msg.setElement(2, address); 1483 msg.setParity(); // Set the parity bit 1484 return msg; 1485 } 1486 1487 /** 1488 * Generate the message to set or request the Computer Interface speed. 1489 * 1490 * @param speed 1 is 19,200bps, 2 is 38,400bps, 3 is 57,600bps, 4 is 1491 * 115,200bps. Send invalid speed to request the current 1492 * setting. 1493 * @return message for set / request interface speed. 1494 */ 1495 public static XNetMessage getLISpeedRequestMsg(int speed) { 1496 XNetMessage msg = new XNetMessage(4); 1497 msg.setElement(0, XNetConstants.LI101_REQUEST); 1498 msg.setElement(1, XNetConstants.LI101_REQUEST_BAUD); 1499 msg.setElement(2, speed); 1500 msg.setParity(); // Set the parity bit 1501 return msg; 1502 } 1503 1504 /** 1505 * Generate text translations of messages for use in the XpressNet monitor. 1506 * 1507 * @return representation of the XNetMessage as a string. 1508 */ 1509 @Override 1510 public String toMonitorString(){ 1511 String text; 1512 /* Start decoding messages sent by the computer */ 1513 /* Start with LI101F requests */ 1514 if (getElement(0) == XNetConstants.LI_VERSION_REQUEST) { 1515 text = Bundle.getMessage("XNetMessageRequestLIVersion"); 1516 } else if (getElement(0) == XNetConstants.LI101_REQUEST) { 1517 switch (getElement(1)) { 1518 case XNetConstants.LI101_REQUEST_ADDRESS: 1519 text = Bundle.getMessage("XNetMessageRequestLIAddress", getElement(2)); 1520 break; 1521 case XNetConstants.LI101_REQUEST_BAUD: 1522 switch (getElement(2)) { 1523 case 1: 1524 text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_LI_BAUD, 1525 Bundle.getMessage("LIBaud19200")); 1526 break; 1527 case 2: 1528 text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_LI_BAUD, 1529 Bundle.getMessage("Baud38400")); 1530 break; 1531 case 3: 1532 text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_LI_BAUD, 1533 Bundle.getMessage("Baud57600")); 1534 break; 1535 case 4: 1536 text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_LI_BAUD, 1537 Bundle.getMessage("Baud115200")); 1538 break; 1539 default: 1540 text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_LI_BAUD, 1541 Bundle.getMessage("BaudOther")); 1542 } 1543 break; 1544 default: 1545 text = toString(); 1546 } 1547 /* Next, we have generic requests */ 1548 } else if (getElement(0) == XNetConstants.CS_REQUEST) { 1549 switch (getElement(1)) { 1550 case XNetConstants.EMERGENCY_OFF: 1551 text = Bundle.getMessage("XNetMessageRequestEmergencyOff"); 1552 break; 1553 case XNetConstants.RESUME_OPS: 1554 text = Bundle.getMessage("XNetMessageRequestNormalOps"); 1555 break; 1556 case XNetConstants.SERVICE_MODE_CSRESULT: 1557 text = Bundle.getMessage("XNetMessageRequestServiceModeResult"); 1558 break; 1559 case XNetConstants.CS_VERSION: 1560 text = Bundle.getMessage("XNetMessageRequestCSVersion"); 1561 break; 1562 case XNetConstants.CS_STATUS: 1563 text = Bundle.getMessage("XNetMessageRequestCSStatus"); 1564 break; 1565 default: 1566 text = toString(); 1567 } 1568 } else if (getElement(0) == XNetConstants.CS_SET_POWERMODE 1569 && getElement(1) == XNetConstants.CS_SET_POWERMODE 1570 && getElement(2) == XNetConstants.CS_POWERMODE_AUTO) { 1571 text = Bundle.getMessage("XNetMessageRequestCSPowerModeAuto"); 1572 } else if (getElement(0) == XNetConstants.CS_SET_POWERMODE 1573 && getElement(1) == XNetConstants.CS_SET_POWERMODE 1574 && getElement(2) == XNetConstants.CS_POWERMODE_MANUAL) { 1575 text = Bundle.getMessage("XNetMessageRequestCSPowerModeManual"); 1576 /* Next, we have Programming Requests */ 1577 } else if (getElement(0) == XNetConstants.PROG_READ_REQUEST) { 1578 switch (getElement(1)) { 1579 case XNetConstants.PROG_READ_MODE_REGISTER: 1580 text = Bundle.getMessage("XNetMessageRequestServiceModeReadRegister",getElement(2)); 1581 break; 1582 case XNetConstants.PROG_READ_MODE_CV: 1583 text = Bundle.getMessage("XNetMessageRequestServiceModeReadDirect",getElement(2)); 1584 break; 1585 case XNetConstants.PROG_READ_MODE_PAGED: 1586 text = Bundle.getMessage("XNetMessageRequestServiceModeReadPaged",getElement(2)); 1587 break; 1588 case XNetConstants.PROG_READ_MODE_CV_V36: 1589 text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_READ_DIRECT_V_36,(getElement(2)== 0 ? 1024 : getElement(2))); 1590 break; 1591 case XNetConstants.PROG_READ_MODE_CV_V36 + 1: 1592 text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_READ_DIRECT_V_36,(256 + getElement(2))); 1593 break; 1594 case XNetConstants.PROG_READ_MODE_CV_V36 + 2: 1595 text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_READ_DIRECT_V_36,(512 + getElement(2))); 1596 break; 1597 case XNetConstants.PROG_READ_MODE_CV_V36 + 3: 1598 text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_READ_DIRECT_V_36,(768 + getElement(2))); 1599 break; 1600 default: 1601 text = toString(); 1602 } 1603 } else if (getElement(0) == XNetConstants.PROG_WRITE_REQUEST) { 1604 switch (getElement(1)) { 1605 case XNetConstants.PROG_WRITE_MODE_REGISTER: 1606 text = Bundle.getMessage("XNetMessageRequestServiceModeWriteRegister",getElement(2),getElement(3)); 1607 break; 1608 case XNetConstants.PROG_WRITE_MODE_CV: 1609 text = Bundle.getMessage("XNetMessageRequestServiceModeWriteDirect",getElement(2),getElement(3)); 1610 break; 1611 case XNetConstants.PROG_WRITE_MODE_PAGED: 1612 text = Bundle.getMessage("XNetMessageRequestServiceModeWritePaged",getElement(2),getElement(3)); 1613 break; 1614 case XNetConstants.PROG_WRITE_MODE_CV_V36: 1615 text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_WRITE_DIRECT_V_36,(getElement(2)== 0 ? 1024 : getElement(2)),getElement(3)); 1616 break; 1617 case (XNetConstants.PROG_WRITE_MODE_CV_V36 + 1): 1618 text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_WRITE_DIRECT_V_36,(256 + getElement(2)),getElement(3)); 1619 break; 1620 case (XNetConstants.PROG_WRITE_MODE_CV_V36 + 2): 1621 text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_WRITE_DIRECT_V_36,(512 + getElement(2)),getElement(3)); 1622 break; 1623 case (XNetConstants.PROG_WRITE_MODE_CV_V36 + 3): 1624 text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_WRITE_DIRECT_V_36,(768 + getElement(2)),getElement(3)); 1625 break; 1626 default: 1627 text = toString(); 1628 } 1629 } else if (getElement(0) == XNetConstants.OPS_MODE_PROG_REQ) { 1630 switch (getElement(1)) { 1631 case XNetConstants.OPS_MODE_PROG_WRITE_REQ: 1632 if ((getElement(4) & 0xEC) == 0xEC 1633 || (getElement(4) & 0xE4) == 0xE4) { 1634 String message = ""; 1635 if ((getElement(4) & 0xEC) == 0xEC) { 1636 message ="XNetMessageOpsModeByteWrite"; 1637 } else if ((getElement(4) & 0xE4) == 0xE4) { 1638 message ="XNetMessageOpsModeByteVerify"; 1639 } 1640 text = Bundle.getMessage(message, 1641 getElement(6), 1642 (1 + getElement(5) + ((getElement(4) & 0x03) << 8)), 1643 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3))); 1644 break; 1645 } else if ((getElement(4) & 0xE8) == 0xE8) { 1646 String message; 1647 if ((getElement(6) & 0x10) == 0x10) { 1648 message ="XNetMessageOpsModeBitVerify"; 1649 } else { 1650 message ="XNetMessageOpsModeBitWrite"; 1651 } 1652 text = Bundle.getMessage(message, 1653 ((getElement(6) & 0x08) >> 3), 1654 (1 + getElement(5) + ((getElement(4) & 0x03) << 8)), 1655 (getElement(6) & 0x07), 1656 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3))); 1657 break; 1658 } 1659 //$FALL-THROUGH$ 1660 default: 1661 text = toString(); 1662 } 1663 // Next, decode the locomotive operation requests 1664 } else if (getElement(0) == XNetConstants.LOCO_OPER_REQ) { 1665 text = "Mobile Decoder Operations Request: "; 1666 int speed; 1667 String direction; 1668 switch (getElement(1)) { 1669 case XNetConstants.LOCO_SPEED_14: 1670 if ((getElement(4) & 0x80) != 0) { 1671 direction = Bundle.getMessage(FORWARD); 1672 } else { 1673 direction = Bundle.getMessage(REVERSE); 1674 } 1675 text = text 1676 + Bundle.getMessage(X_NET_MESSAGE_SET_SPEED, 1677 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3))) 1678 + " " + (getElement(4) & 0x0f) 1679 + " " + Bundle.getMessage(X_NET_MESSAGE_SET_DIRECTION, direction); 1680 text += " " + Bundle.getMessage(SPEED_STEP_MODE_X, 14) + "."; 1681 break; 1682 case XNetConstants.LOCO_SPEED_27: 1683 text = text 1684 + Bundle.getMessage(X_NET_MESSAGE_SET_SPEED, 1685 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3))) 1686 + " "; 1687 if(log.isDebugEnabled()) { 1688 log.debug("{}", LenzCommandStation.calcLocoAddress(getElement(2), getElement(3))); // address printed as: "1234" = OK 1689 log.debug(text); // address printed as: "1,234" = WRONG 1690 } 1691 speed 1692 = (((getElement(4) & 0x10) >> 4) 1693 + ((getElement(4) & 0x0F) << 1)); 1694 if (speed >= 3) { 1695 speed -= 3; 1696 } 1697 text += speed; 1698 if ((getElement(4) & 0x80) != 0) { 1699 text += " " + Bundle.getMessage(X_NET_MESSAGE_SET_DIRECTION, Bundle.getMessage(FORWARD)); 1700 } else { 1701 text += " " + Bundle.getMessage(X_NET_MESSAGE_SET_DIRECTION, Bundle.getMessage(REVERSE)); 1702 } 1703 text += " " + Bundle.getMessage(SPEED_STEP_MODE_X, 27) + "."; 1704 break; 1705 case XNetConstants.LOCO_SPEED_28: 1706 text = text 1707 + Bundle.getMessage(X_NET_MESSAGE_SET_SPEED, 1708 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3))) 1709 + " "; 1710 speed 1711 = (((getElement(4) & 0x10) >> 4) 1712 + ((getElement(4) & 0x0F) << 1)); 1713 if (speed >= 3) { 1714 speed -= 3; 1715 } 1716 text += speed; 1717 if ((getElement(4) & 0x80) != 0) { 1718 text += " " + Bundle.getMessage(X_NET_MESSAGE_SET_DIRECTION, Bundle.getMessage(FORWARD)); 1719 } else { 1720 text += " " + Bundle.getMessage(X_NET_MESSAGE_SET_DIRECTION, Bundle.getMessage(REVERSE)); 1721 } 1722 text += " " + Bundle.getMessage(SPEED_STEP_MODE_X, 28) + "."; 1723 break; 1724 case XNetConstants.LOCO_SPEED_128: 1725 if ((getElement(4) & 0x80) != 0) { 1726 direction = Bundle.getMessage(FORWARD); 1727 } else { 1728 direction = Bundle.getMessage(REVERSE); 1729 } 1730 text = text 1731 + Bundle.getMessage(X_NET_MESSAGE_SET_SPEED, 1732 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3))) 1733 + " " 1734 + (getElement(4) & 0x7F) + " " + Bundle.getMessage(X_NET_MESSAGE_SET_DIRECTION, direction); 1735 text += " " + Bundle.getMessage(SPEED_STEP_MODE_X, 128) + "."; 1736 break; 1737 case XNetConstants.LOCO_SET_FUNC_GROUP1: 1738 text = text + buildSetFunctionGroup1MonitorString(); 1739 break; 1740 case XNetConstants.LOCO_SET_FUNC_GROUP2: 1741 text = text + buildSetFunctionGroup2MonitorString(); 1742 break; 1743 case XNetConstants.LOCO_SET_FUNC_GROUP3: 1744 text = text + buildSetFunctionGroup3MonitorString(); 1745 break; 1746 case XNetConstants.LOCO_SET_FUNC_GROUP4: 1747 text = text + buildSetFunctionGroup4MonitorString(); 1748 break; 1749 case XNetConstants.LOCO_SET_FUNC_GROUP5: 1750 text = text + buildSetFunctionGroup5MonitorString(); 1751 break; 1752 case XNetConstants.LOCO_SET_FUNC_Group1: 1753 text = text + buildSetFunctionGroup1MomentaryMonitorString(); 1754 break; 1755 case XNetConstants.LOCO_SET_FUNC_Group2: 1756 text = text + buildSetFunctionGroup2MomentaryMonitorString(); 1757 break; 1758 case XNetConstants.LOCO_SET_FUNC_Group3: 1759 text = text + buildSetFunctionGroup3MomentaryMonitorString(); 1760 break; 1761 case XNetConstants.LOCO_SET_FUNC_Group4: 1762 text = text + buildSetFunctionGroup4MomentaryMonitorString(); 1763 break; 1764 case XNetConstants.LOCO_SET_FUNC_Group5: 1765 text = text + buildSetFunctionGroup5MomentaryMonitorString(); 1766 break; 1767 case XNetConstants.LOCO_ADD_MULTI_UNIT_REQ: 1768 text = Bundle.getMessage("XNetMessageAddToConsistDirNormalRequest", 1769 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)), 1770 getElement(4)); 1771 break; 1772 case (XNetConstants.LOCO_ADD_MULTI_UNIT_REQ | 0x01): 1773 text = Bundle.getMessage("XNetMessageAddToConsistDirReverseRequest", 1774 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)), 1775 getElement(4)); 1776 break; 1777 case (XNetConstants.LOCO_REM_MULTI_UNIT_REQ): 1778 text = Bundle.getMessage("XNetMessageRemoveFromConsistRequest", 1779 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)), 1780 getElement(4)); 1781 break; 1782 case (XNetConstants.LOCO_IN_MULTI_UNIT_REQ_FORWARD): 1783 text = Bundle.getMessage("XNetMessageSearchCSStackForwardNextMULoco", 1784 getElement(2), 1785 LenzCommandStation.calcLocoAddress(getElement(3), getElement(4))); 1786 break; 1787 case (XNetConstants.LOCO_IN_MULTI_UNIT_REQ_BACKWARD): 1788 text = Bundle.getMessage("XNetMessageSearchCSStackBackwardNextMULoco", 1789 getElement(2), 1790 LenzCommandStation.calcLocoAddress(getElement(3), getElement(4))); 1791 break; 1792 default: 1793 text = toString(); 1794 } 1795 // Emergency Stop a locomotive 1796 } else if (getElement(0) == XNetConstants.EMERGENCY_STOP) { 1797 text = Bundle.getMessage("XNetMessageAddressedEmergencyStopRequest", 1798 LenzCommandStation.calcLocoAddress(getElement(1), getElement(2))); 1799 // Disolve or Establish a Double Header 1800 } else if (getElement(0) == XNetConstants.LOCO_DOUBLEHEAD 1801 && getElement(1) == XNetConstants.LOCO_DOUBLEHEAD_BYTE2) { 1802 int loco1 = LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)); 1803 int loco2 = LenzCommandStation.calcLocoAddress(getElement(4), getElement(5)); 1804 if (loco2 == 0) { 1805 text = Bundle.getMessage("XNetMessageDisolveDoubleHeaderRequest",loco1); 1806 } else { 1807 text = Bundle.getMessage("XNetMessageBuildDoubleHeaderRequest",loco1,loco2); 1808 } 1809 // Locomotive Status Request messages 1810 } else if (getElement(0) == XNetConstants.LOCO_STATUS_REQ) { 1811 switch (getElement(1)) { 1812 case XNetConstants.LOCO_INFO_REQ_FUNC: 1813 text = Bundle.getMessage("XNetMessageRequestLocoFunctionMomStatus", 1814 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3))); 1815 break; 1816 case XNetConstants.LOCO_INFO_REQ_FUNC_HI_ON: 1817 text = Bundle.getMessage("XNetMessageRequestLocoFunctionHighStatus", 1818 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3))); 1819 break; 1820 case XNetConstants.LOCO_INFO_REQ_FUNC_HI_MOM: 1821 text = Bundle.getMessage("XNetMessageRequestLocoFunctionHighMomStatus", 1822 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3))); 1823 break; 1824 case XNetConstants.LOCO_INFO_REQ_V3: 1825 text = Bundle.getMessage("XNetMessageRequestLocoInfo", 1826 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3))); 1827 break; 1828 case XNetConstants.LOCO_STACK_SEARCH_FWD: 1829 text = Bundle.getMessage("XNetMessageSearchCSStackForward", 1830 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3))); 1831 break; 1832 case XNetConstants.LOCO_STACK_SEARCH_BKWD: 1833 text = Bundle.getMessage("XNetMessageSearchCSStackBackward", 1834 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3))); 1835 break; 1836 case XNetConstants.LOCO_STACK_DELETE: 1837 text = Bundle.getMessage("XNetMessageDeleteAddressOnStack", 1838 LenzCommandStation.calcLocoAddress(getElement(2), getElement(3))); 1839 break; 1840 default: 1841 text = toString(); 1842 } 1843 } else if(getElement(0) == XNetConstants.CS_MULTI_UNIT_REQ) { 1844 if (getElement(1) == XNetConstants.CS_MULTI_UNIT_REQ_FWD){ 1845 text = Bundle.getMessage("XNetMessageSearchCSStackForwardConsistAddress", 1846 getElement(2)); 1847 } else if(getElement(1) == XNetConstants.CS_MULTI_UNIT_REQ_BKWD){ 1848 text = Bundle.getMessage("XNetMessageSearchCSStackBackwardConsistAddress", 1849 getElement(2)); 1850 } else { 1851 text = toString(); 1852 } 1853 // Accessory Info Request message 1854 } else if (getElement(0) == XNetConstants.ACC_INFO_REQ) { 1855 String nibblekey=(((getElement(2) & 0x01) == 0x01) ? "FeedbackEncoderUpperNibble" : "FeedbackEncoderLowerNibble"); 1856 text = Bundle.getMessage("XNetMessageFeedbackRequest", 1857 getElement(1), 1858 Bundle.getMessage(nibblekey)); 1859 } else if (getElement(0) == XNetConstants.ACC_OPER_REQ) { 1860 String messageKey =(((getElement(2) & 0x08) == 0x08) ? "XNetMessageAccessoryDecoderOnRequest" : "XNetMessageAccessoryDecoderOffRequest"); 1861 int baseaddress = getElement(1); 1862 int subaddress = ((getElement(2) & 0x06) >> 1); 1863 int address = (baseaddress * 4) + subaddress + 1; 1864 int output = (getElement(2) & 0x01); 1865 text = Bundle.getMessage(messageKey,address, baseaddress,subaddress,output); 1866 } else if (getElement(0) == XNetConstants.ALL_ESTOP) { 1867 text = Bundle.getMessage("XNetMessageRequestEmergencyStop"); 1868 } else { 1869 text = toString(); 1870 } 1871 return text; 1872 } 1873 1874 private String buildSetFunctionGroup1MonitorString() { 1875 String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X, 1) + 1876 " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " "; 1877 int element4 = getElement(4); 1878 if ((element4 & 0x10) != 0) { 1879 text += "F0 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1880 } else { 1881 text += "F0 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1882 } 1883 if ((element4 & 0x01) != 0) { 1884 text += "F1 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1885 } else { 1886 text += "F1 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1887 } 1888 if ((element4 & 0x02) != 0) { 1889 text += "F2 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1890 } else { 1891 text += "F2 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1892 } 1893 if ((element4 & 0x04) != 0) { 1894 text += "F3 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1895 } else { 1896 text += "F3 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1897 } 1898 if ((element4 & 0x08) != 0) { 1899 text += "F4 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1900 } else { 1901 text += "F4 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1902 1903 } 1904 return text; 1905 } 1906 1907 private String buildSetFunctionGroup2MonitorString(){ 1908 String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X, 2) + " " 1909 + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " "; 1910 int element4 = getElement(4); 1911 if ((element4 & 0x01) != 0) { 1912 text += "F5 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1913 } else { 1914 text += "F5 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1915 } 1916 if ((element4 & 0x02) != 0) { 1917 text += "F6 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1918 } else { 1919 text += "F6 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1920 } 1921 if ((element4 & 0x04) != 0) { 1922 text += "F7 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1923 } else { 1924 text += "F7 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1925 } 1926 if ((element4 & 0x08) != 0) { 1927 text += "F8 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1928 } else { 1929 text += "F8 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1930 } 1931 return text; 1932 } 1933 1934 private String buildSetFunctionGroup3MonitorString() { 1935 String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X, 3) + " " 1936 + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " "; 1937 int element4 = getElement(4); 1938 if ((element4 & 0x01) != 0) { 1939 text += "F9 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1940 } else { 1941 text += "F9 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1942 } 1943 if ((element4 & 0x02) != 0) { 1944 text += "F10 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1945 } else { 1946 text += "F10 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1947 } 1948 if ((element4 & 0x04) != 0) { 1949 text += "F11 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1950 } else { 1951 text += "F11 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1952 } 1953 if ((element4 & 0x08) != 0) { 1954 text += "F12 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1955 } else { 1956 text += "F12 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1957 } 1958 return text; 1959 } 1960 1961 private String buildSetFunctionGroup4MonitorString() { 1962 String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X, 4) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " "; 1963 int element4 = getElement(4); 1964 if ((element4 & 0x01) != 0) { 1965 text += "F13 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1966 } else { 1967 text += "F13 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1968 } 1969 if ((element4 & 0x02) != 0) { 1970 text += "F14 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1971 } else { 1972 text += "F14 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1973 } 1974 if ((element4 & 0x04) != 0) { 1975 text += "F15 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1976 } else { 1977 text += "F15 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1978 } 1979 if ((element4 & 0x08) != 0) { 1980 text += "F16 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1981 } else { 1982 text += "F16 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1983 } 1984 if ((element4 & 0x10) != 0) { 1985 text += "F17 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1986 } else { 1987 text += "F17 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1988 } 1989 if ((element4 & 0x20) != 0) { 1990 text += "F18 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1991 } else { 1992 text += "F18 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1993 } 1994 if ((element4 & 0x40) != 0) { 1995 text += "F19 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 1996 } else { 1997 text += "F19 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 1998 } 1999 if ((element4 & 0x80) != 0) { 2000 text += "F20 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 2001 } else { 2002 text += "F20 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 2003 } 2004 return text; 2005 } 2006 2007 private String buildSetFunctionGroup5MonitorString() { 2008 String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X, 5) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " "; 2009 int element4 = getElement(4); 2010 if ((element4 & 0x01) != 0) { 2011 text += "F21 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 2012 } else { 2013 text += "F21 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 2014 } 2015 if ((element4 & 0x02) != 0) { 2016 text += "F22 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 2017 } else { 2018 text += "F22 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 2019 } 2020 if ((element4 & 0x04) != 0) { 2021 text += "F23 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 2022 } else { 2023 text += "F23 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 2024 } 2025 if ((element4 & 0x08) != 0) { 2026 text += "F24 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 2027 } else { 2028 text += "F24 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 2029 } 2030 if ((element4 & 0x10) != 0) { 2031 text += "F25 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 2032 } else { 2033 text += "F25 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 2034 } 2035 if ((element4 & 0x20) != 0) { 2036 text += "F26 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 2037 } else { 2038 text += "F26 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 2039 } 2040 if ((element4 & 0x40) != 0) { 2041 text += "F27 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 2042 } else { 2043 text += "F27 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 2044 } 2045 if ((element4 & 0x80) != 0) { 2046 text += "F28 " + Bundle.getMessage(POWER_STATE_ON) + "; "; 2047 } else { 2048 text += "F28 " + Bundle.getMessage(POWER_STATE_OFF) + "; "; 2049 } 2050 return text; 2051 } 2052 2053 private String buildSetFunctionGroup1MomentaryMonitorString() { 2054 String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY, 1) + " " 2055 + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " "; 2056 int element4 = getElement(4); 2057 if ((element4 & 0x10) == 0) { 2058 text += "F0 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2059 } else { 2060 text += "F0 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2061 } 2062 if ((element4 & 0x01) == 0) { 2063 text += "F1 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2064 } else { 2065 text += "F1 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2066 } 2067 if ((element4 & 0x02) == 0) { 2068 text += "F2 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2069 } else { 2070 text += "F2 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2071 } 2072 if ((element4 & 0x04) == 0) { 2073 text += "F3 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2074 } else { 2075 text += "F3 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2076 } 2077 if ((element4 & 0x08) == 0) { 2078 text += "F4 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2079 } else { 2080 text += "F4 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2081 } 2082 return text; 2083 } 2084 2085 private String buildSetFunctionGroup2MomentaryMonitorString() { 2086 String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY, 2) + " " 2087 + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " "; 2088 int element4 = getElement(4); 2089 if ((element4 & 0x01) == 0) { 2090 text += "F5 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2091 } else { 2092 text += "F5 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2093 } 2094 if ((element4 & 0x02) == 0) { 2095 text += "F6 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2096 } else { 2097 text += "F6 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2098 } 2099 if ((element4 & 0x04) == 0) { 2100 text += "F7 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2101 } else { 2102 text += "F7 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2103 } 2104 if ((element4 & 0x08) == 0) { 2105 text += "F8 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2106 } else { 2107 text += "F8 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2108 } 2109 return text; 2110 } 2111 2112 private String buildSetFunctionGroup3MomentaryMonitorString() { 2113 String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY, 3) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " "; 2114 int element4 = getElement(4); 2115 if ((element4 & 0x01) == 0) { 2116 text += "F9 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2117 } else { 2118 text += "F9 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2119 } 2120 if ((element4 & 0x02) == 0) { 2121 text += "F10 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2122 } else { 2123 text += "F10 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2124 } 2125 if ((element4 & 0x04) == 0) { 2126 text += "F11 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2127 } else { 2128 text += "F11 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2129 } 2130 if ((element4 & 0x08) == 0) { 2131 text += "F12 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2132 } else { 2133 text += "F12 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2134 } 2135 return text; 2136 } 2137 2138 2139 private String buildSetFunctionGroup4MomentaryMonitorString() { 2140 String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY, 4) + " " 2141 + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " "; 2142 int element4 = getElement(4); 2143 if ((element4 & 0x01) == 0) { 2144 text += "F13 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2145 } else { 2146 text += "F13 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2147 } 2148 if ((element4 & 0x02) == 0) { 2149 text += "F14 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2150 } else { 2151 text += "F14 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2152 } 2153 if ((element4 & 0x04) == 0) { 2154 text += "F15 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2155 } else { 2156 text += "F15 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2157 } 2158 if ((element4 & 0x08) == 0) { 2159 text += "F16 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2160 } else { 2161 text += "F16 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2162 } 2163 if ((element4 & 0x10) == 0) { 2164 text += "F17 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2165 } else { 2166 text += "F17 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2167 } 2168 if ((element4 & 0x20) == 0) { 2169 text += "F18 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2170 } else { 2171 text += "F18 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2172 } 2173 if ((element4 & 0x40) == 0) { 2174 text += "F19 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2175 } else { 2176 text += "F19 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2177 } 2178 if ((element4 & 0x80) == 0) { 2179 text += "F20 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2180 } else { 2181 text += "F20 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2182 } 2183 return text; 2184} 2185 2186 private String buildSetFunctionGroup5MomentaryMonitorString() { 2187 String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY, 5) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " "; 2188 int element4 = getElement(4); 2189 if ((element4 & 0x01) == 0) { 2190 text += "F21 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2191 } else { 2192 text += "F21 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2193 } 2194 if ((element4 & 0x02) == 0) { 2195 text += "F22 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2196 } else { 2197 text += "F22 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2198 } 2199 if ((element4 & 0x04) == 0) { 2200 text += "F23 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2201 } else { 2202 text += "F23 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2203 } 2204 if ((element4 & 0x08) == 0) { 2205 text += "F24 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2206 } else { 2207 text += "F24 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2208 } 2209 if ((element4 & 0x10) == 0) { 2210 text += "F25 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2211 } else { 2212 text += "F25 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2213 } 2214 if ((element4 & 0x20) == 0) { 2215 text += "F26 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2216 } else { 2217 text += "F26 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2218 } 2219 if ((element4 & 0x40) == 0) { 2220 text += "F27 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2221 } else { 2222 text += "F27 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2223 } 2224 if ((element4 & 0x80) == 0) { 2225 text += "F28 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; "; 2226 } else { 2227 text += "F28 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; "; 2228 } 2229 return text; 2230 } 2231 2232 // initialize logging 2233 private static final Logger log = LoggerFactory.getLogger(XNetMessage.class); 2234 2235}