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