001package jmri.jmrix.can.cbus; 002 003import jmri.ProgrammingMode; 004import jmri.jmrix.AbstractMessage; 005import jmri.jmrix.can.CanFrame; 006import jmri.jmrix.can.CanMessage; 007import jmri.jmrix.can.CanMutableFrame; 008import jmri.jmrix.can.CanReply; 009 010 011/** 012 * Class to allow use of CBUS concepts to access the underlying can message. 013 * <p> 014 * Methods that take a CanMessage or CanReply as argument: 015 * <ul> 016 * <li>CanMessage - Can Frame being sent by JMRI 017 * <li>CanReply - Can Frame being received by JMRI 018 * </ul> 019 * https://github.com/MERG-DEV/CBUSlib. 020 * 021 * @author Andrew Crosland Copyright (C) 2008 022 * @author Steve Young (C) 2018 023 */ 024public class CbusMessage { 025 026 /** 027 * Return a CanReply for use in sensors, turnouts + light 028 * If a response event, set to normal event 029 * In future, this may also translate extended messages down to normal messages. 030 * 031 * @param original CanReply to be coverted to normal opc 032 * @return new CanReply perhaps converted from response OPC to normal OPC. 033 */ 034 public static CanReply opcRangeToStl(CanReply original){ 035 CanReply msg = new CanReply(original); 036 int opc = getOpcode(msg); 037 // log.debug(" about to check opc {} ",opc); 038 switch (opc) { 039 case CbusConstants.CBUS_ARON: 040 msg.setElement(0, CbusConstants.CBUS_ACON); 041 break; 042 case CbusConstants.CBUS_AROF: 043 msg.setElement(0, CbusConstants.CBUS_ACOF); 044 break; 045 case CbusConstants.CBUS_ARSON: 046 msg.setElement(0, CbusConstants.CBUS_ASON); 047 break; 048 case CbusConstants.CBUS_ARSOF: 049 msg.setElement(0, CbusConstants.CBUS_ASOF); 050 break; 051 default: 052 break; 053 } 054 return msg; 055 } 056 057 058 /** 059 * Get the Op Code from the CanMessage 060 * 061 * @param am CanMessage or CanReply 062 * @return OPC of the message 063 */ 064 public static int getOpcode(AbstractMessage am) { 065 return am.getElement(0); 066 } 067 068 /** 069 * Get the Data Length from the CanMessage 070 * 071 * @param am CanMessage or CanReply 072 * @return the message data length 073 */ 074 public static int getDataLength(AbstractMessage am) { 075 return am.getElement(0) >> 5; 076 } 077 078 /** 079 * Get the Node Number from a CanFrame Event 080 * 081 * @param am CanMessage or CanReply 082 * @return the node number if not a short event 083 */ 084 public static int getNodeNumber(AbstractMessage am) { 085 if (isEvent(am) && !isShort(am) ) { 086 return am.getElement(1) * 256 + am.getElement(2); 087 } else { 088 return 0; 089 } 090 } 091 092 /** 093 * Get the Event Number from a CBUS Event 094 * 095 * @param m CanMessage or CanReply 096 * @return the message event ( device ) number, else -1 if not an event. 097 */ 098 public static int getEvent(AbstractMessage m) { 099 if (isEvent(m)) { 100 return m.getElement(3) * 256 + m.getElement(4); 101 } else { 102 return -1; 103 } 104 } 105 106 /** 107 * Get the Event Type ( on or off ) from a CanFrame 108 * 109 * @param am CanFrame or CanReply 110 * @return CbusConstant EVENT_ON or EVENT_OFF 111 */ 112 public static int getEventType(AbstractMessage am) { 113 if ( CbusOpCodes.isOnEvent(am.getElement(0))) { 114 return CbusConstants.EVENT_ON; 115 } else { 116 return CbusConstants.EVENT_OFF; 117 } 118 } 119 120 /** 121 * Tests if a CanMessage or CanReply is an Event. 122 * Performs Extended and RTR check. 123 * Adheres to cbus spec, ie on off responses to an AREQ are events. 124 * 125 * @param am CanMessage or CanReply 126 * @return True if event, else False. 127 */ 128 public static boolean isEvent(AbstractMessage am) { 129 if ( am instanceof CanFrame && ((CanFrame)am).extendedOrRtr()){ 130 return false; 131 } 132 return CbusOpCodes.isEvent(am.getElement(0)); 133 } 134 135 /** 136 * Tests if CanFrame is a short event 137 * 138 * @param am CanReply or CanMessage 139 * @return true if Short Event, else false 140 */ 141 public static boolean isShort(AbstractMessage am) { 142 return CbusOpCodes.isShortEvent(am.getElement(0)); 143 } 144 145 /** 146 * Set the CAN ID within a CanMessage or CanReply Header 147 * 148 * @param am CanMessage or CanReply 149 * @param id CAN ID 150 */ 151 public static void setId(AbstractMessage am, int id) throws IllegalArgumentException { 152 if (am instanceof CanMutableFrame){ 153 CanMutableFrame m = (CanMutableFrame) am; 154 int update = m.getHeader(); 155 if (m.isExtended()) { 156 throw new IllegalArgumentException("No CAN ID Concept on Extended CBUS CAN Frame."); 157 } else { 158 if ((id & ~0x7f) != 0) { 159 throw new IllegalArgumentException("invalid standard ID value: " + id); 160 } 161 m.setHeader((update & ~0x07f) | id); 162 } 163 } 164 else { 165 throw new IllegalArgumentException(am + " is Not a CanMutableFrame"); 166 } 167 } 168 169 /** 170 * Set the priority within a CanMessage or CanReply Header. 171 * 172 * @param am CanMessage or CanReply 173 * @param pri Priority 174 */ 175 public static void setPri(AbstractMessage am, int pri) throws IllegalArgumentException { 176 if (am instanceof CanMutableFrame){ 177 CanMutableFrame m = (CanMutableFrame) am; 178 if ((pri & ~0x0F) != 0) { 179 throw new IllegalArgumentException("Invalid CBUS Priority value: " + pri); 180 } 181 if (m.isExtended()) { 182 throw new IllegalArgumentException("Extended CBUS CAN Frames do not have a priority concept."); 183 } else { 184 m.setHeader((m.getHeader() & ~0x780) | (pri << 7)); 185 } 186 } 187 else { 188 throw new IllegalArgumentException(am + " is Not a CanMutableFrame"); 189 } 190 } 191 192 /** 193 * Returns string form of a CanMessage ( a Can Frame sent by JMRI ) 194 * Short / Long events converted to Sensor / Turnout / Light hardware address 195 * message priority not indicated 196 * @param m CanReply or CanMessage 197 * @return String of hardware address form 198 */ 199 public static String toAddress(AbstractMessage m) { 200 switch (m.getElement(0)) { 201 case CbusConstants.CBUS_ACON: 202 // + form 203 return "+n" + (m.getElement(1) * 256 + m.getElement(2)) + "e" + (m.getElement(3) * 256 + m.getElement(4)); 204 case CbusConstants.CBUS_ACOF: 205 // - form 206 return "-n" + (m.getElement(1) * 256 + m.getElement(2)) + "e" + (m.getElement(3) * 256 + m.getElement(4)); 207 case CbusConstants.CBUS_ASON: 208 // + short form 209 return "+" + (m.getElement(3) * 256 + m.getElement(4)); 210 case CbusConstants.CBUS_ASOF: 211 // - short form 212 return "-" + (m.getElement(3) * 256 + m.getElement(4)); 213 default: 214 // hex form 215 String tmp = m.toString().replaceAll("\\s*\\[[^\\]]*\\]\\s*", ""); // remove the [header] 216 return "X" + tmp.replaceAll(" ", ""); 217 } 218 } 219 220 /** 221 * Checks if a CanMessage is requesting Track Power Off 222 * 223 * @param m Can Frame Message 224 * @return boolean 225 */ 226 public static boolean isRequestTrackOff(CanMessage m) { 227 return m.getOpCode() == CbusConstants.CBUS_RTOF; 228 } 229 230 /** 231 * Checks if a CanMessage is requesting Track Power On 232 * 233 * @param m Can Frame Message 234 * @return True if outgoing track power on request 235 */ 236 public static boolean isRequestTrackOn(CanMessage m) { 237 return m.getOpCode() == CbusConstants.CBUS_RTON; 238 } 239 240 /** 241 * Get the CAN ID within a CanReply or CanMessage Header 242 * 243 * @param f CanReply or CanMessage 244 * @return CAN ID of the outgoing message 245 */ 246 public static int getId(AbstractMessage f) throws IllegalArgumentException { 247 if (f instanceof CanFrame){ 248 CanFrame cfMsg = (CanFrame) f; 249 if (cfMsg.isExtended()) { 250 return cfMsg.getHeader() & 0x1FFFFF; 251 } else { 252 return cfMsg.getHeader() & 0x7f; 253 } 254 } 255 else { 256 throw new IllegalArgumentException(f + " is Not a CanFrame"); 257 } 258 } 259 260 /** 261 * Get the priority from within the CanReply or CanMessage Header 262 * 263 * @param r CanReply or CanMessage 264 * @return Priority of the outgoing message 265 */ 266 public static int getPri(AbstractMessage r) throws IllegalArgumentException { 267 if (r instanceof CanFrame){ 268 CanFrame cfMsg = (CanFrame) r; 269 if (cfMsg.isExtended()) { 270 return (cfMsg.getHeader() >> 25) & 0x0F; 271 } else { 272 return (cfMsg.getHeader() >> 7) & 0x0F; 273 } 274 } 275 else { 276 throw new IllegalArgumentException(r + " is Not a CanFrame"); 277 } 278 } 279 280 /** 281 * Tests if CanReply is confirming Track Power Off. 282 * 283 * @param m CanReply 284 * @return True if is a Track Off notification 285 */ 286 public static boolean isTrackOff(CanReply m) { 287 return m.getOpCode() == CbusConstants.CBUS_TOF; 288 } 289 290 /** 291 * Tests if CanReply is confirming Track Power On. 292 * 293 * @param m CanReply 294 * @return True if is a Track On notification 295 */ 296 public static boolean isTrackOn(CanReply m) { 297 return m.getOpCode() == CbusConstants.CBUS_TON; 298 } 299 300 /** 301 * Tests if CanReply is a System Reset 302 * 303 * @param m CanReply 304 * @return True if emergency Stop 305 */ 306 public static boolean isArst(CanReply m) { 307 return m.getOpCode() == CbusConstants.CBUS_ARST; 308 } 309 310 /** 311 * CBUS programmer commands 312 * @param cv CV to read 313 * @param mode Programming Mode 314 * @param header CAN ID 315 * @return CanMessage ready to send 316 */ 317 static public CanMessage getReadCV(int cv, ProgrammingMode mode, int header) { 318 CanMessage m = new CanMessage(5, header); 319 m.setElement(0, CbusConstants.CBUS_QCVS); 320 m.setElement(1, CbusConstants.SERVICE_HANDLE); 321 m.setElement(2, (cv / 256) & 0xff); 322 m.setElement(3, cv & 0xff); 323 if (mode.equals(ProgrammingMode.PAGEMODE)) { 324 m.setElement(4, CbusConstants.CBUS_PROG_PAGED); 325 } else if (mode.equals(ProgrammingMode.DIRECTBITMODE)) { 326 m.setElement(4, CbusConstants.CBUS_PROG_DIRECT_BIT); 327 } else if (mode.equals(ProgrammingMode.DIRECTBYTEMODE)) { 328 m.setElement(4, CbusConstants.CBUS_PROG_DIRECT_BYTE); 329 } else { 330 m.setElement(4, CbusConstants.CBUS_PROG_REGISTER); 331 } 332 setPri(m, 0xb); 333 return m; 334 } 335 336 /** 337 * CBUS programmer commands 338 * 339 * CBUS VCVS works like a QCVS read but the programmer will first check if 340 * the CV contents are equal to the startVal. This can speed up CV reads by 341 * skipping reading of other values. 342 * 343 * @param cv CV to read 344 * @param mode Programming Mode 345 * @param startVal Hint of current CV value 346 * @param header CAN ID 347 * @return CanMessage ready to send 348 */ 349 static public CanMessage getVerifyCV(int cv, ProgrammingMode mode, int startVal, int header) { 350 CanMessage m = new CanMessage(6, header); 351 m.setElement(0, CbusConstants.CBUS_VCVS); 352 m.setElement(1, CbusConstants.SERVICE_HANDLE); 353 m.setElement(2, (cv / 256) & 0xff); 354 m.setElement(3, cv & 0xff); 355 if (mode.equals(ProgrammingMode.PAGEMODE)) { 356 m.setElement(4, CbusConstants.CBUS_PROG_PAGED); 357 } else if (mode.equals(ProgrammingMode.DIRECTBITMODE)) { 358 m.setElement(4, CbusConstants.CBUS_PROG_DIRECT_BIT); 359 } else if (mode.equals(ProgrammingMode.DIRECTBYTEMODE)) { 360 m.setElement(4, CbusConstants.CBUS_PROG_DIRECT_BYTE); 361 } else { 362 m.setElement(4, CbusConstants.CBUS_PROG_REGISTER); 363 } 364 m.setElement(5, startVal & 0xff); 365 setPri(m, 0xb); 366 return m; 367 } 368 369 /** 370 * Get a CanMessage to write a CV. 371 * @param cv Which CV, 0-65534 372 * @param val New CV value, 0-255 373 * @param mode Programming Mode 374 * @param header CAN ID 375 * @return ready to send CanMessage 376 */ 377 static public CanMessage getWriteCV(int cv, int val, ProgrammingMode mode, int header) { 378 CanMessage m = new CanMessage(6, header); 379 m.setElement(0, CbusConstants.CBUS_WCVS); 380 m.setElement(1, CbusConstants.SERVICE_HANDLE); 381 m.setElement(2, (cv / 256) & 0xff); 382 m.setElement(3, cv & 0xff); 383 if (mode.equals(ProgrammingMode.PAGEMODE)) { 384 m.setElement(4, CbusConstants.CBUS_PROG_PAGED); 385 } else if (mode.equals(ProgrammingMode.DIRECTBITMODE)) { 386 m.setElement(4, CbusConstants.CBUS_PROG_DIRECT_BIT); 387 } else if (mode.equals(ProgrammingMode.DIRECTBYTEMODE)) { 388 m.setElement(4, CbusConstants.CBUS_PROG_DIRECT_BYTE); 389 } else { 390 m.setElement(4, CbusConstants.CBUS_PROG_REGISTER); 391 } 392 m.setElement(5, val); 393 setPri(m, 0xb); 394 return m; 395 } 396 397 /** 398 * CBUS Ops mode programmer commands 399 * @param mAddress Loco Address, non-DCC format 400 * @param mLongAddr If Loco Address is a long address 401 * @param header CAN ID 402 * @param val New CV value 403 * @param cv Which CV, 0-65534 404 * @return ready to send CanMessage 405 */ 406 static public CanMessage getOpsModeWriteCV(int mAddress, boolean mLongAddr, int cv, int val, int header) { 407 CanMessage m = new CanMessage(7, header); 408 int address = mAddress; 409 m.setElement(0, CbusConstants.CBUS_WCVOA); 410 if (mLongAddr) { 411 address = address | 0xc000; 412 } 413 m.setElement(1, address / 256); 414 m.setElement(2, address & 0xff); 415 m.setElement(3, (cv / 256) & 0xff); 416 m.setElement(4, cv & 0xff); 417 m.setElement(5, CbusConstants.CBUS_OPS_BYTE); 418 m.setElement(6, val); 419 setPri(m, 0xb); 420 return m; 421 } 422 423 /** 424 * Get a CanMessage to send track power on 425 * 426 * @param header for connection CAN ID 427 * @return the CanMessage to send to request track power on 428 */ 429 static public CanMessage getRequestTrackOn(int header) { 430 CanMessage m = new CanMessage(1, header); 431 m.setElement(0, CbusConstants.CBUS_RTON); 432 setPri(m, 0xb); 433 return m; 434 } 435 436 /** 437 * Get a CanMessage to send track power off 438 * 439 * @param header for connection CAN ID 440 * @return the CanMessage to send to request track power off 441 */ 442 static public CanMessage getRequestTrackOff(int header) { 443 CanMessage m = new CanMessage(1, header); 444 m.setElement(0, CbusConstants.CBUS_RTOF); 445 setPri(m, 0xb); 446 return m; 447 } 448 449 450 // CBUS bootloader commands 451 452 /** 453 * This is a strict CBUS message to put a node into boot mode. 454 * @param nn Node Number 1-65534 455 * @param header CAN ID 456 * @return ready to send CanMessage 457 */ 458 static public CanMessage getBootEntry(int nn, int header) { 459 CanMessage m = new CanMessage(3, header); 460 m.setElement(0, CbusConstants.CBUS_BOOTM); 461 m.setElement(1, (nn / 256) & 0xFF); 462 m.setElement(2, nn & 0xFF); 463 setPri(m, 0xb); 464 return m; 465 } 466 467 /** 468 * Microchip AN247 format NOP message to set address. 469 * <p> 470 * The CBUS bootloader uses extended ID frames 471 * 472 * @param a address 473 * @param header CAN ID - overridden by call to setHeader 474 * @return ready to send CanMessage 475 */ 476 static public CanMessage getBootNop(int a, int header) { 477 CanMessage m = new CanMessage(8, header); 478 m.setExtended(true); 479 m.setHeader(0x4); 480 m.setElement(0, a & 0xFF); 481 m.setElement(1, (a / 256) & 0xFF); 482 m.setElement(2, (a / 65536) & 0xFF); 483 m.setElement(3, 0); 484 //m.setElement(4, 0x0D); 485 m.setElement(4, CbusConstants.CBUS_BOOT_MODE_ACK 486 | CbusConstants.CBUS_BOOT_MODE_AUTO_INC 487 | CbusConstants.CBUS_BOOT_MODE_AUTO_ERASE 488 | CbusConstants.CBUS_BOOT_MODE_WRT_UNLCK 489 ); 490 m.setElement(5, CbusConstants.CBUS_BOOT_NOP); 491 m.setElement(6, 0); 492 m.setElement(7, 0); 493 return m; 494 } 495 496 /** 497 * Microchip AN247 format message to reset and enter normal mode. 498 * 499 * @param header CAN ID - overridden by call to setHeader 500 * @return ready to send CanMessage 501 */ 502 static public CanMessage getBootReset(int header) { 503 CanMessage m = new CanMessage(8, header); 504 m.setExtended(true); 505 m.setHeader(0x4); 506 m.setElement(0, 0); 507 m.setElement(1, 0); 508 m.setElement(2, 0); 509 m.setElement(3, 0); 510 //m.setElement(4, 0x0D); 511 m.setElement(4, CbusConstants.CBUS_BOOT_MODE_ACK 512 | CbusConstants.CBUS_BOOT_MODE_AUTO_INC 513 | CbusConstants.CBUS_BOOT_MODE_AUTO_ERASE 514 | CbusConstants.CBUS_BOOT_MODE_WRT_UNLCK 515 ); 516 m.setElement(5, CbusConstants.CBUS_BOOT_RESET); 517 m.setElement(6, 0); 518 m.setElement(7, 0); 519 return m; 520 } 521 522 /** 523 * Microchip AN247 format message to initialise the bootloader and set the 524 * start address. 525 * 526 * @param a start address 527 * @param header CAN ID - overridden by call to setHeader 528 * @return ready to send CanMessage 529 */ 530 static public CanMessage getBootInitialise(int a, int header) { 531 CanMessage m = new CanMessage(8, header); 532 m.setExtended(true); 533 m.setHeader(0x4); 534 m.setElement(0, a & 0xFF); 535 m.setElement(1, (a / 256) & 0xFF); 536 m.setElement(2, (a / 65536) & 0xFF); 537 m.setElement(3, 0); 538 //m.setElement(4, 0x0D); 539 m.setElement(4, CbusConstants.CBUS_BOOT_MODE_ACK 540 | CbusConstants.CBUS_BOOT_MODE_AUTO_INC 541 | CbusConstants.CBUS_BOOT_MODE_AUTO_ERASE 542 | CbusConstants.CBUS_BOOT_MODE_WRT_UNLCK 543 ); 544 m.setElement(5, CbusConstants.CBUS_BOOT_INIT); 545 m.setElement(6, 0); 546 m.setElement(7, 0); 547 return m; 548 } 549 550 /** 551 * Microchip AN247 format message to send the checksum for comparison. 552 * 553 * At time of writing [6th Feb '20] The MERG bootloader doc is incorrect and 554 * shows the checksum as being byte swapped. 555 * 556 * @param c 0-65535 2's complement of sum of all program bytes sent 557 * @param header CAN ID - overridden by call to setHeader 558 * @return ready to send CanMessage 559 */ 560 static public CanMessage getBootCheck(int c, int header) { 561 CanMessage m = new CanMessage(8, header); 562 m.setExtended(true); 563 m.setHeader(0x4); 564 m.setElement(0, 0); 565 m.setElement(1, 0); 566 m.setElement(2, 0); 567 m.setElement(3, 0); 568 //m.setElement(4, 0x0D); 569 m.setElement(4, CbusConstants.CBUS_BOOT_MODE_ACK 570 | CbusConstants.CBUS_BOOT_MODE_AUTO_INC 571 | CbusConstants.CBUS_BOOT_MODE_AUTO_ERASE 572 | CbusConstants.CBUS_BOOT_MODE_WRT_UNLCK 573 ); 574 m.setElement(5, CbusConstants.CBUS_BOOT_CHECK); 575 m.setElement(6, c & 0xff); 576 m.setElement(7, (c >> 8) & 0xff); 577 return m; 578 } 579 580 /** 581 * Microchip AN247 format message to check if a module is in boot mode. 582 * 583 * @param header CAN ID - overridden by call to setHeader 584 * @return ready to send CanMessage 585 */ 586 static public CanMessage getBootTest(int header) { 587 CanMessage m = new CanMessage(8, header); 588 m.setExtended(true); 589 m.setHeader(0x4); 590 m.setElement(0, 0); 591 m.setElement(1, 0); 592 m.setElement(2, 0); 593 m.setElement(3, 0); 594 //m.setElement(4, 0x0D); 595 m.setElement(4, CbusConstants.CBUS_BOOT_MODE_ACK 596 | CbusConstants.CBUS_BOOT_MODE_AUTO_INC 597 | CbusConstants.CBUS_BOOT_MODE_AUTO_ERASE 598 | CbusConstants.CBUS_BOOT_MODE_WRT_UNLCK 599 ); 600 m.setElement(5, CbusConstants.CBUS_BOOT_TEST); 601 m.setElement(6, 0); 602 m.setElement(7, 0); 603 return m; 604 } 605 606 /** 607 * CBUS bootloader v1.0 format message to request device ID. 608 * 609 * @param header CAN ID - overridden by call to setHeader 610 * @return ready to send CanMessage 611 */ 612 static public CanMessage getBootDevId(int header) { 613 CanMessage m = new CanMessage(8, header); 614 m.setExtended(true); 615 m.setHeader(0x4); 616 m.setElement(0, 0); 617 m.setElement(1, 0); 618 m.setElement(2, 0); 619 m.setElement(3, 0); 620 //m.setElement(4, 0x0D); 621 m.setElement(4, CbusConstants.CBUS_BOOT_MODE_ACK 622 | CbusConstants.CBUS_BOOT_MODE_AUTO_INC 623 | CbusConstants.CBUS_BOOT_MODE_AUTO_ERASE 624 | CbusConstants.CBUS_BOOT_MODE_WRT_UNLCK 625 ); 626 m.setElement(5, CbusConstants.CBUS_BOOT_DEVID); 627 m.setElement(6, 0); 628 m.setElement(7, 0); 629 return m; 630 } 631 632 /** 633 * CBUS bootloader v1.0 format message to request bootloader ID. 634 * 635 * @param header CAN ID - overridden by call to setHeader 636 * @return ready to send CanMessage 637 */ 638 static public CanMessage getBootId(int header) { 639 CanMessage m = new CanMessage(8, header); 640 m.setExtended(true); 641 m.setHeader(0x4); 642 m.setElement(0, 0); 643 m.setElement(1, 0); 644 m.setElement(2, 0); 645 m.setElement(3, 0); 646 //m.setElement(4, 0x0D); 647 m.setElement(4, CbusConstants.CBUS_BOOT_MODE_ACK 648 | CbusConstants.CBUS_BOOT_MODE_AUTO_INC 649 | CbusConstants.CBUS_BOOT_MODE_AUTO_ERASE 650 | CbusConstants.CBUS_BOOT_MODE_WRT_UNLCK 651 ); 652 m.setElement(5, CbusConstants.CBUS_BOOT_BOOTID); 653 m.setElement(6, 0); 654 m.setElement(7, 0); 655 return m; 656 } 657 658 /** 659 * CBUS bootloader v1.0 format message to set memory region write enables 660 * 661 * @param enables enable bits for memory regions 662 * @param header CAN ID - overridden by call to setHeader 663 * @return ready to send CanMessage 664 */ 665 static public CanMessage getBootEnables(int enables, int header) { 666 CanMessage m = new CanMessage(8, header); 667 m.setExtended(true); 668 m.setHeader(0x4); 669 m.setElement(0, 0); 670 m.setElement(1, 0); 671 m.setElement(2, 0); 672 m.setElement(3, 0); 673 //m.setElement(4, 0x0D); 674 m.setElement(4, CbusConstants.CBUS_BOOT_MODE_ACK 675 | CbusConstants.CBUS_BOOT_MODE_AUTO_INC 676 | CbusConstants.CBUS_BOOT_MODE_AUTO_ERASE 677 | CbusConstants.CBUS_BOOT_MODE_WRT_UNLCK 678 ); 679 m.setElement(5, CbusConstants.CBUS_BOOT_ENABLES); 680 m.setElement(6, enables & 0xFF); 681 m.setElement(7, 0); 682 return m; 683 } 684 685 /** 686 * Microchip AN247 format message to write 8 bytes of data 687 * 688 * @param d data array, 8 length, values 0-255 689 * @param header CAN ID - overridden by call to setHeader 690 * @return ready to send CanMessage 691 */ 692 static public CanMessage getBootWriteData(int[] d, int header) { 693 CanMessage m = new CanMessage(d.length, header); 694 m.setExtended(true); 695 m.setHeader(0x5); 696 for (int i = 0; i < d.length; i++) { 697 m.setElement(i, d[i] & 0xff); 698 } 699 return m; 700 } 701 702 /** 703 * Microchip AN247 format message to write up to 8 bytes of data 704 * 705 * @param d data array, values 0-255 706 * @param header CAN ID - overridden by call to setHeader 707 * @return ready to send CanMessage 708 */ 709 static public CanMessage getBootWriteData(byte[] d, int header) { 710 CanMessage m = new CanMessage(d.length, header); 711 m.setExtended(true); 712 m.setHeader(0x5); 713 for (int i = 0; i < d.length; i++) { 714 m.setElement(i, d[i] & 0xff); 715 } 716 return m; 717 } 718 719 /** 720 * Tests if a message is a bootloader data write 721 * 722 * @param m message 723 * @return true if the message is a bootloader data write 724 */ 725 public static boolean isBootWriteData(CanMessage m) { 726 if (m.isExtended() && (m.getHeader() == 0x5)) { 727 return (true); 728 } 729 return (false); 730 } 731 732 /** 733 * Tests if incoming CanReply is a Boot Command Error. 734 * 735 * @param r CanReply 736 * @return True if is a Boot Command Error 737 */ 738 public static boolean isBootError(CanReply r) { 739 if (r.isExtended() && (r.getHeader() == 0x10000004) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOT_ERROR) 740 && (r.getNumDataElements() == 1)) { 741 return (true); 742 } 743 return (false); 744 } 745 746 /** 747 * Tests if incoming CanReply is a Boot Data Error. 748 * 749 * @param r CanReply 750 * @return True if is a Boot Data Error 751 */ 752 public static boolean isBootDataError(CanReply r) { 753 if (r.isExtended() && (r.getHeader() == 0x10000005) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOT_ERROR) 754 && (r.getNumDataElements() == 1)) { 755 return (true); 756 } 757 return (false); 758 } 759 760 /** 761 * Tests if incoming CanReply is a Boot Command OK. 762 * 763 * @param r CanReply 764 * @return True if is a Boot COmmand OK 765 */ 766 public static boolean isBootOK(CanReply r) { 767 if (r.isExtended() && (r.getHeader() == 0x10000004) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOT_OK) 768 && (r.getNumDataElements() == 1)) { 769 return (true); 770 } 771 return (false); 772 } 773 774 /** 775 * Tests if incoming CanReply is a Boot Data OK. 776 * 777 * @param r CanReply 778 * @return True if is a Boot Data OK 779 */ 780 public static boolean isBootDataOK(CanReply r) { 781 if (r.isExtended() && (r.getHeader() == 0x10000005) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOT_OK) 782 && (r.getNumDataElements() == 1)) { 783 return (true); 784 } 785 return (false); 786 } 787 788 /** 789 * Tests if incoming CanReply is a Boot Out of Range 790 * 791 * @param r CanReply 792 * @return True if is a Boot Data OK 793 */ 794 public static boolean isBootOutOfRange(CanReply r) { 795 if (r.isExtended() && (r.getHeader() == 0x10000004) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOT_OUT_OF_RANGE) 796 && (r.getNumDataElements() == 1)) { 797 return (true); 798 } 799 return (false); 800 } 801 802 /** 803 * Tests if incoming CanReply is a Boot Out of Range 804 * 805 * @param r CanReply 806 * @return True if is a Boot Data OK 807 */ 808 public static boolean isBootDataOutOfRange(CanReply r) { 809 if (r.isExtended() && (r.getHeader() == 0x10000005) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOT_OUT_OF_RANGE) 810 && (r.getNumDataElements() == 1)) { 811 return (true); 812 } 813 return (false); 814 } 815 816 /** 817 * Tests if incoming CanReply is a Boot Confirm. 818 * 819 * @param r CanReply 820 * @return True if is a Boot Confirm 821 */ 822 public static boolean isBootConfirm(CanReply r) { 823 if (r.isExtended() && (r.getHeader() == 0x10000004) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOTC) 824 && (r.getNumDataElements() == 1)) { 825 return (true); 826 } 827 return (false); 828 } 829 830 /** 831 * Tests if incoming CanReply is a device ID reply. 832 * 833 * @param r CanReply 834 * @return True if is a Boot Confirm 835 */ 836 public static boolean isBootDevId(CanReply r) { 837 if (r.isExtended() && (r.getHeader() == 0x10000004) && (r.getElement(0) == CbusConstants.CBUS_EXT_DEVID) 838 && (r.getNumDataElements() == 7)) { 839 return (true); 840 } 841 return (false); 842 } 843 844 /** 845 * Tests if incoming CanReply is a bootloader ID reply. 846 * 847 * @param r CanReply 848 * @return True if is a Boot Confirm 849 */ 850 public static boolean isBootId(CanReply r) { 851 if (r.isExtended() && (r.getHeader() == 0x10000004) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOTID) 852 && (r.getNumDataElements() == 5)) { 853 return (true); 854 } 855 return (false); 856 } 857 858// private final static Logger log = LoggerFactory.getLogger(CbusMessage.class); 859}