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(5, CbusConstants.CBUS_BOOT_NOP);
486        m.setElement(6, 0);
487        m.setElement(7, 0);
488        return m;
489    }
490
491    /**
492     * Microchip AN247 format message to reset and enter normal mode.
493     *
494     * @param header CAN ID - overridden by call to setHeader
495     * @return ready to send CanMessage
496     */
497    static public CanMessage getBootReset(int header) {
498        CanMessage m = new CanMessage(8, header);
499        m.setExtended(true);
500        m.setHeader(0x4);
501        m.setElement(0, 0);
502        m.setElement(1, 0);
503        m.setElement(2, 0);
504        m.setElement(3, 0);
505        m.setElement(4, 0x0D);
506        m.setElement(5, CbusConstants.CBUS_BOOT_RESET);
507        m.setElement(6, 0);
508        m.setElement(7, 0);
509        return m;
510    }
511
512    /**
513     * Microchip AN247 format message to initialise the bootloader and set the
514     * start address.
515     *
516     * @param a start address
517     * @param header CAN ID - overridden by call to setHeader
518     * @return ready to send CanMessage
519     */
520    static public CanMessage getBootInitialise(int a, int header) {
521        CanMessage m = new CanMessage(8, header);
522        m.setExtended(true);
523        m.setHeader(0x4);
524        m.setElement(0, a & 0xFF);
525        m.setElement(1, (a / 256) & 0xFF);
526        m.setElement(2, (a / 65536) & 0xFF);
527        m.setElement(3, 0);
528        m.setElement(4, 0x0D);
529        m.setElement(5, CbusConstants.CBUS_BOOT_INIT);
530        m.setElement(6, 0);
531        m.setElement(7, 0);
532        return m;
533    }
534
535    /**
536     * Microchip AN247 format message to send the checksum for comparison.
537     *
538     * At time of writing [6th Feb '20] The MERG bootloader doc is incorrect and
539     * shows the checksum as being byte swapped.
540     *
541     * @param c 0-65535 2's complement of sum of all program bytes sent
542     * @param header CAN ID - overridden by call to setHeader
543     * @return ready to send CanMessage
544     */
545    static public CanMessage getBootCheck(int c, int header) {
546        CanMessage m = new CanMessage(8, header);
547        m.setExtended(true);
548        m.setHeader(0x4);
549        m.setElement(0, 0);
550        m.setElement(1, 0);
551        m.setElement(2, 0);
552        m.setElement(3, 0);
553        m.setElement(4, 0x0D);
554        m.setElement(5, CbusConstants.CBUS_BOOT_CHECK);
555        m.setElement(6, c & 0xff);
556        m.setElement(7, (c >> 8) & 0xff);
557        return m;
558    }
559
560    /**
561     * Microchip AN247 format message to check if a module is in boot mode.
562     *
563     * @param header CAN ID - overridden by call to setHeader
564     * @return ready to send CanMessage
565     */
566    static public CanMessage getBootTest(int header) {
567        CanMessage m = new CanMessage(8, header);
568        m.setExtended(true);
569        m.setHeader(0x4);
570        m.setElement(0, 0);
571        m.setElement(1, 0);
572        m.setElement(2, 0);
573        m.setElement(3, 0);
574        m.setElement(4, 0x0D);
575        m.setElement(5, CbusConstants.CBUS_BOOT_TEST);
576        m.setElement(6, 0);
577        m.setElement(7, 0);
578        return m;
579    }
580
581    /**
582     * CBUS bootloader v1.0 format message to request device ID.
583     *
584     * @param header CAN ID - overridden by call to setHeader
585     * @return ready to send CanMessage
586     */
587    static public CanMessage getBootDevId(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(5, CbusConstants.CBUS_BOOT_DEVID);
597        m.setElement(6, 0);
598        m.setElement(7, 0);
599        return m;
600    }
601
602    /**
603     * CBUS bootloader v1.0 format message to request bootloader ID.
604     *
605     * @param header CAN ID - overridden by call to setHeader
606     * @return ready to send CanMessage
607     */
608    static public CanMessage getBootId(int header) {
609        CanMessage m = new CanMessage(8, header);
610        m.setExtended(true);
611        m.setHeader(0x4);
612        m.setElement(0, 0);
613        m.setElement(1, 0);
614        m.setElement(2, 0);
615        m.setElement(3, 0);
616        m.setElement(4, 0x0D);
617        m.setElement(5, CbusConstants.CBUS_BOOT_BOOTID);
618        m.setElement(6, 0);
619        m.setElement(7, 0);
620        return m;
621    }
622
623    /**
624     * CBUS bootloader v1.0 format message to set memory region write enables
625     *
626     * @param enables enable bits for memory regions
627     * @param header CAN ID - overridden by call to setHeader
628     * @return ready to send CanMessage
629     */
630    static public CanMessage getBootEnables(int enables, int header) {
631        CanMessage m = new CanMessage(8, header);
632        m.setExtended(true);
633        m.setHeader(0x4);
634        m.setElement(0, 0);
635        m.setElement(1, 0);
636        m.setElement(2, 0);
637        m.setElement(3, 0);
638        m.setElement(4, 0x0D);
639        m.setElement(5, CbusConstants.CBUS_BOOT_ENABLES);
640        m.setElement(6, enables & 0xFF);
641        m.setElement(7, 0);
642        return m;
643    }
644
645    /**
646     * Microchip AN247 format message to write 8 bytes of data
647     *
648     * @param d data array, 8 length, values 0-255
649     * @param header CAN ID - overridden by call to setHeader
650     * @return ready to send CanMessage
651     */
652    static public CanMessage getBootWriteData(int[] d, int header) {
653        CanMessage m = new CanMessage(d.length, header);
654        m.setExtended(true);
655        m.setHeader(0x5);
656        for (int i = 0; i < d.length; i++) {
657            m.setElement(i, d[i] & 0xff);
658        }
659        return m;
660    }
661
662    /**
663     * Microchip AN247 format message to write up to 8 bytes of data
664     *
665     * @param d data array, values 0-255
666     * @param header CAN ID - overridden by call to setHeader
667     * @return ready to send CanMessage
668     */
669    static public CanMessage getBootWriteData(byte[] d, int header) {
670        CanMessage m = new CanMessage(d.length, header);
671        m.setExtended(true);
672        m.setHeader(0x5);
673        for (int i = 0; i < d.length; i++) {
674            m.setElement(i, d[i] & 0xff);
675        }
676        return m;
677    }
678
679    /**
680     * Tests if a message is a bootloader data write
681     *
682     * @param m message
683     * @return true if the message is a bootloader data write
684     */
685    public static boolean isBootWriteData(CanMessage m) {
686        if (m.isExtended() && (m.getHeader() == 0x5)) {
687            return (true);
688        }
689        return (false);
690    }
691
692    /**
693     * Tests if incoming CanReply is a Boot Command Error.
694     *
695     * @param r CanReply
696     * @return True if is a Boot Command Error
697     */
698    public static boolean isBootError(CanReply r) {
699        if (r.isExtended() && (r.getHeader() == 0x10000004) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOT_ERROR)
700                && (r.getNumDataElements() == 1)) {
701            return (true);
702        }
703        return (false);
704    }
705
706    /**
707     * Tests if incoming CanReply is a Boot Data Error.
708     *
709     * @param r CanReply
710     * @return True if is a Boot Data Error
711     */
712    public static boolean isBootDataError(CanReply r) {
713        if (r.isExtended() && (r.getHeader() == 0x10000005) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOT_ERROR)
714                && (r.getNumDataElements() == 1)) {
715            return (true);
716        }
717        return (false);
718    }
719
720    /**
721     * Tests if incoming CanReply is a Boot Command OK.
722     *
723     * @param r CanReply
724     * @return True if is a Boot COmmand OK
725     */
726    public static boolean isBootOK(CanReply r) {
727        if (r.isExtended() && (r.getHeader() == 0x10000004) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOT_OK)
728                && (r.getNumDataElements() == 1)) {
729            return (true);
730        }
731        return (false);
732    }
733
734    /**
735     * Tests if incoming CanReply is a Boot Data OK.
736     *
737     * @param r CanReply
738     * @return True if is a Boot Data OK
739     */
740    public static boolean isBootDataOK(CanReply r) {
741        if (r.isExtended() && (r.getHeader() == 0x10000005) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOT_OK)
742                && (r.getNumDataElements() == 1)) {
743            return (true);
744        }
745        return (false);
746    }
747
748    /**
749     * Tests if incoming CanReply is a Boot Out of Range
750     *
751     * @param r CanReply
752     * @return True if is a Boot Data OK
753     */
754    public static boolean isBootOutOfRange(CanReply r) {
755        if (r.isExtended() && (r.getHeader() == 0x10000004) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOT_OUT_OF_RANGE)
756                && (r.getNumDataElements() == 1)) {
757            return (true);
758        }
759        return (false);
760    }
761
762    /**
763     * Tests if incoming CanReply is a Boot Out of Range
764     *
765     * @param r CanReply
766     * @return True if is a Boot Data OK
767     */
768    public static boolean isBootDataOutOfRange(CanReply r) {
769        if (r.isExtended() && (r.getHeader() == 0x10000005) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOT_OUT_OF_RANGE)
770                && (r.getNumDataElements() == 1)) {
771            return (true);
772        }
773        return (false);
774    }
775
776    /**
777     * Tests if incoming CanReply is a Boot Confirm.
778     *
779     * @param r CanReply
780     * @return True if is a Boot Confirm
781     */
782    public static boolean isBootConfirm(CanReply r) {
783        if (r.isExtended() && (r.getHeader() == 0x10000004) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOTC)
784                && (r.getNumDataElements() == 1)) {
785            return (true);
786        }
787        return (false);
788    }
789
790    /**
791     * Tests if incoming CanReply is a device ID reply.
792     *
793     * @param r CanReply
794     * @return True if is a Boot Confirm
795     */
796    public static boolean isBootDevId(CanReply r) {
797        if (r.isExtended() && (r.getHeader() == 0x10000004) && (r.getElement(0) == CbusConstants.CBUS_EXT_DEVID)
798                && (r.getNumDataElements() == 7)) {
799            return (true);
800        }
801        return (false);
802    }
803
804    /**
805     * Tests if incoming CanReply is a bootloader ID reply.
806     *
807     * @param r CanReply
808     * @return True if is a Boot Confirm
809     */
810    public static boolean isBootId(CanReply r) {
811        if (r.isExtended() && (r.getHeader() == 0x10000004) && (r.getElement(0) == CbusConstants.CBUS_EXT_BOOTID)
812                && (r.getNumDataElements() == 5)) {
813            return (true);
814        }
815        return (false);
816    }
817
818//    private final static Logger log = LoggerFactory.getLogger(CbusMessage.class);
819}