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}