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