001package jmri.jmrix.dccpp;
002
003import java.util.regex.Matcher;
004import java.util.regex.Pattern;
005import java.util.regex.PatternSyntaxException;
006import org.slf4j.Logger;
007import org.slf4j.LoggerFactory;
008
009import jmri.configurexml.AbstractXmlAdapter;
010
011/**
012 * Represents a single response from the DCC++ system.
013 *
014 * @author Paul Bender Copyright (C) 2004
015 * @author Mark Underwood Copyright (C) 2015
016 * @author Harald Barth Copyright (C) 2019
017 *
018 * Based on XNetReply
019 */
020
021/*
022 * A few notes on implementation
023 *
024 * DCCppReply objects are (usually) created by parsing a String that is the result
025 * of an incoming reply message from the Base Station.
026 * The information is stored as a String, along with a Regex string that allows
027 * the individual data elements to be extracted when needed.
028 *
029 * Listeners and other higher level code should first check to make sure the
030 * DCCppReply is of the correct type by calling the relevant isMessageType() method.
031 * Then, call the various getThisDataElement() method to retrieve the data of
032 * interest.
033 *
034 * For example, to get the Speed from a Throttle Reply, first check that it
035 * /is/ a ThrottleReply by calling message.isThrottleReply(), and then
036 * get the speed by calling message.getSpeedInt() or getSpeedString().
037 *
038 * The reason for all of this misdirection is to make sure that the upper layer
039 * JMRI code is isolated/insulated from any changes in the actual Base Station
040 * message format.  For example, there is no need for the listener code to know
041 * that the speed is the second number after the "T" in the reply (nor that a
042 * Throttle reply starts with a "T").
043 */
044
045public class DCCppReply extends jmri.jmrix.AbstractMRReply {
046
047    protected String myRegex;
048    protected StringBuilder myReply;
049   
050    // Create a new reply.
051    public DCCppReply() {
052        super();
053        setBinary(false);
054        myRegex = "";
055        myReply = new StringBuilder();
056    }
057
058    // Create a new reply from an existing reply
059    public DCCppReply(DCCppReply reply) {
060        super(reply);
061        setBinary(false);
062        myRegex = reply.myRegex;
063        myReply = reply.myReply;
064    }
065
066    // Create a new reply from a string
067    public DCCppReply(String reply) {
068        super();
069        setBinary(false);
070        myRegex = "";
071        myReply = new StringBuilder(reply);
072        _nDataChars = toString().length();
073    }
074
075    @Override
076    public String toString() {
077        log.trace("DCCppReply.toString(): msg '{}'", myReply);
078        return myReply.toString();
079    }
080
081   /**
082    * Generate text translations of replies for use in the DCCpp monitor.
083    *
084    * @return representation of the DCCppReply as a string.
085    **/
086    @Override
087    public String toMonitorString(){
088        // Beautify and display
089        String text;
090
091        switch (getOpCodeChar()) {
092            case DCCppConstants.THROTTLE_REPLY:
093                text = "Throttle Reply: ";
094                text += "Register: " + getRegisterString() + ", ";
095                text += "Speed: " + getSpeedString() + ", ";
096                text += "Direction: " + getDirectionString();
097                break;
098            case DCCppConstants.TURNOUT_REPLY:
099                if (isTurnoutDefReply()) {
100                    text = "Turnout Reply: ";
101                    text += "Number: " + getTOIDString() + ", ";
102                    text += "Address: " + getTOAddressString() + ", ";
103                    text += "Index: " + getTOAddressIndexString() + ", ";
104                    // if we are able to parse the address and index we can convert it
105                    // to a standard DCC address for display.
106                    if (getTOAddressInt() != -1 && getTOAddressIndexInt() != -1) {
107                        int boardAddr = getTOAddressInt();
108                        int boardIndex = getTOAddressIndexInt();
109                        int dccAddress = (((boardAddr - 1) * 4) + boardIndex) + 1;
110                        text += "DCC Address: " + dccAddress + ", ";
111                    }
112                    text += "Direction: " + getTOStateString();
113                } else {
114                    text = "Turnout Reply: ";
115                    text += "Number: " + getTOIDString() + ", ";
116                    text += "Direction: " + getTOStateString();
117                }
118                break;
119            case DCCppConstants.SENSOR_REPLY_H:
120                text = "Sensor Reply (Inactive): ";
121                text += "Number: " + getSensorNumString() + ", ";
122                text += "State: INACTIVE";
123                break;
124            case DCCppConstants.SENSOR_REPLY_L:
125                // Also covers the V1.0 version SENSOR_REPLY
126                if (isSensorDefReply()) {
127                    text = "Sensor Def Reply: ";
128                    text += "Number: " + getSensorDefNumString() + ", ";
129                    text += "Pin: " + getSensorDefPinString() + ", ";
130                    text += "Pullup: " + getSensorDefPullupString();
131                } else {
132                    text = "Sensor Reply (Active): ";
133                    text += "Number: " + getSensorNumString() + ", ";
134                    text += "State: ACTIVE";
135                }
136                break;
137            case DCCppConstants.OUTPUT_REPLY:
138                if (isOutputCmdReply()) {
139                    text = "Output Command Reply: ";
140                    text += "Number: " + getOutputNumString() + ", ";
141                    text += "State: " + getOutputCmdStateString();
142                } else if (isOutputListReply()) {
143                    text = "Output Command Reply: ";
144                    text += "Number: " + getOutputNumString() + ", ";
145                    text += "Pin: " + getOutputListPinString() + ", ";
146                    text += "Flags: " + getOutputListIFlagString() + ", ";
147                    text += "State: " + getOutputListStateString();
148                } else {
149                    text = "Invalid Output Reply Format: ";
150                    text += toString();
151                }
152                break;
153            case DCCppConstants.PROGRAM_REPLY:
154                if (isProgramBitReply()) {
155                    text = "Program Bit Reply: ";
156                    text += "Callback Num: " + getCallbackNumString() + ", ";
157                    text += "Callback Sub: " + getCallbackSubString() + ", ";
158                    text += "CV: " + getCVString() + ", ";
159                    text += "CV Bit: " + getProgramBitString() + ", ";
160                } else {
161                    text = "Program Reply: ";
162                    text += "Callback Num: " + getCallbackNumString() + ", ";
163                    text += "Callback Sub: " + getCallbackSubString() + ", ";
164                    text += "CV: " + getCVString() + ", ";
165                }
166                text += "Value: " + getReadValueString();
167                break;
168            case DCCppConstants.VERIFY_REPLY:
169                text = "Prog Verify Reply: ";
170                text += "CV: " + getCVString() + ", ";
171                text += "Value: " + getReadValueString();
172                break;
173            case DCCppConstants.STATUS_REPLY:
174                text = "Status:";
175                text += "Station: " + getStationType();
176                text += ", Build: " + getBuildString();
177                text += ", Version: " + getVersion();
178                break;
179            case DCCppConstants.POWER_REPLY:
180                if(isNamedPowerReply()) {
181                    text = "Power Status: ";
182                    text += "Name:" + getPowerDistrictName();
183                    text += "Status:" + getPowerDistrictStatus();
184                } else {
185                    text = "Power Status: ";
186                    text += (getPowerBool() ? "ON" : "OFF");
187                }
188                break;
189            case DCCppConstants.CURRENT_REPLY:
190                text = "Current: " + getCurrentString() + " / 1024";
191                break;
192            case DCCppConstants.METER_REPLY:
193                text = String.format("Meter reply: name %s, value %.2f, type %s, unit %s, min %.2f, max %.2f, resolution %.2f, warn %.2f", 
194                        getMeterName(), getMeterValue(), getMeterType(),  
195                        getMeterUnit(), getMeterMinValue(), getMeterMaxValue(), 
196                        getMeterResolution(), getMeterWarnValue());
197                break;
198            // case DCCppConstants.LISTPACKET_REPLY:
199            //     // TODO: Implement this fully
200            //     text = "List Packet Reply...\n";
201            //     break;
202            case DCCppConstants.WRITE_EEPROM_REPLY:
203                text = "Write EEPROM Reply... ";
204                // TODO: Don't use getProgValueString()
205                text += "Turnouts: " + getValueString(1) + ", ";
206                text += "Sensors: " + getValueString(2) + ", ";
207                text += "Outputs: " + getValueString(3);
208                break;
209            case DCCppConstants.COMM_TYPE_REPLY:
210                text = "Comm Type Reply ";
211                text += "Type: " + getCommTypeInt();
212                text += " Port: " + getCommTypeValueString();
213                break;
214            case DCCppConstants.MADC_FAIL_REPLY:
215                text = "No Sensor/Turnout/Output Reply ";
216                break;
217            case DCCppConstants.MADC_SUCCESS_REPLY:
218                text = "Sensor/Turnout/Output MADC Success Reply ";
219                break;
220            case DCCppConstants.MAXNUMSLOTS_REPLY:
221                text = "Number of slots reply: " + getValueString(1);
222                break;
223            case DCCppConstants.DIAG_REPLY:
224                text = "DIAG: " + getValueString(1);
225                break;
226            default:
227                text = "Unrecognized reply: '" + toString() + "'";
228        }
229
230        return text;
231    }
232
233    public void parseReply(String s) {
234        DCCppReply r = DCCppReply.parseDCCppReply(s);
235        log.debug("in parseReply() string: {}", s);
236        if (r != null) {
237            this.myRegex = r.myRegex;
238            this.myReply = r.myReply;
239            this._nDataChars = r._nDataChars;
240            log.trace("copied: this: {}", this);
241        }
242    }
243
244    ///
245    ///
246    /// TODO: Stopped Refactoring to StringBuilder here 12/12/15
247    ///
248    ///
249
250    /**
251     * Parses a string and generates a DCCppReply from the string contents
252     *
253     * @param s String to be parsed
254     * @return DCCppReply or empty string if not a valid formatted string
255     */
256    public static DCCppReply parseDCCppReply(String s) {
257
258        if (log.isTraceEnabled()) {
259            log.trace("Parse charAt(0): {}", s.charAt(0));
260        }
261        DCCppReply r = new DCCppReply(s);
262        switch (s.charAt(0)) {
263            case DCCppConstants.STATUS_REPLY:
264                if (s.matches(DCCppConstants.STATUS_REPLY_BSC_REGEX)) {
265                    log.debug("BSC Status Reply: '{}'", r);
266                    r.myRegex = DCCppConstants.STATUS_REPLY_BSC_REGEX;
267                } else if (s.matches(DCCppConstants.STATUS_REPLY_ESP32_REGEX)) {
268                    log.debug("ESP32 Status Reply: '{}'", r);
269                    r.myRegex = DCCppConstants.STATUS_REPLY_ESP32_REGEX;
270                } else if (s.matches(DCCppConstants.STATUS_REPLY_REGEX)) {
271                    log.debug("Original Status Reply: '{}'", r);
272                    r.myRegex = DCCppConstants.STATUS_REPLY_REGEX;
273                } else if (s.matches(DCCppConstants.STATUS_REPLY_DCCEX_REGEX)) {
274                    log.debug("DCC-EX Status Reply: '{}'", r);
275                    r.myRegex = DCCppConstants.STATUS_REPLY_DCCEX_REGEX;
276                } 
277                return(r);
278            case DCCppConstants.THROTTLE_REPLY:
279                if (s.matches(DCCppConstants.THROTTLE_REPLY_REGEX)) {
280                   log.debug("Throttle Reply: '{}'", r);
281                   r.myRegex = DCCppConstants.THROTTLE_REPLY_REGEX;
282                }
283                return(r);
284            case DCCppConstants.TURNOUT_REPLY:
285                // the order of checking the reply here is critical as both the TURNOUT_DEF_REPLY
286                // and TURNOUT_REPLY regex strings start with the same strings but have different
287                // meanings.
288                if (s.matches(DCCppConstants.TURNOUT_DEF_REPLY_REGEX)) {
289                    r.myRegex = DCCppConstants.TURNOUT_DEF_REPLY_REGEX;
290                } else if (s.matches(DCCppConstants.TURNOUT_REPLY_REGEX)) {
291                    r.myRegex = DCCppConstants.TURNOUT_REPLY_REGEX;
292                } else if (s.matches(DCCppConstants.MADC_FAIL_REPLY_REGEX)) {
293                    r.myRegex = DCCppConstants.MADC_FAIL_REPLY_REGEX;
294                }
295                log.debug("Parsed Reply: '{}' length {}", r.toString(), r._nDataChars);
296                return(r);
297            case DCCppConstants.OUTPUT_REPLY:
298                if (s.matches(DCCppConstants.OUTPUT_LIST_REPLY_REGEX)) {
299                    r.myRegex = DCCppConstants.OUTPUT_LIST_REPLY_REGEX;
300                } else if (s.matches(DCCppConstants.OUTPUT_REPLY_REGEX)) {
301                    r.myRegex = DCCppConstants.OUTPUT_REPLY_REGEX;
302                }
303                log.debug("Parsed Reply: '{}' length {}", r, r._nDataChars);
304                return(r);
305            case DCCppConstants.PROGRAM_REPLY:
306                if (s.matches(DCCppConstants.PROGRAM_BIT_REPLY_REGEX)) {
307                    log.debug("Matches ProgBitReply");
308                    r.myRegex = DCCppConstants.PROGRAM_BIT_REPLY_REGEX;
309                } else if (s.matches(DCCppConstants.PROGRAM_REPLY_REGEX)) {
310                    log.debug("Matches ProgReply");
311                    r.myRegex = DCCppConstants.PROGRAM_REPLY_REGEX;
312                } else {
313                    log.debug("Does not match ProgReply Regex");
314                }
315                return(r);
316            case DCCppConstants.VERIFY_REPLY:
317                if (s.matches(DCCppConstants.PROGRAM_VERIFY_REGEX)) {
318                    log.debug("Matches VerifyReply");
319                    r.myRegex = DCCppConstants.PROGRAM_VERIFY_REGEX;
320                } else {
321                    log.debug("Does not match VerifyReply Regex");
322                }
323                return(r);
324            case DCCppConstants.POWER_REPLY:
325                if (s.matches(DCCppConstants.TRACK_POWER_REPLY_NAMED_REGEX)) {
326                    r.myRegex = DCCppConstants.TRACK_POWER_REPLY_NAMED_REGEX;
327                } else if (s.matches(DCCppConstants.TRACK_POWER_REPLY_REGEX)) {
328                        r.myRegex = DCCppConstants.TRACK_POWER_REPLY_REGEX;
329                }
330                return(r);
331            case DCCppConstants.CURRENT_REPLY:
332                if (s.matches(DCCppConstants.CURRENT_REPLY_NAMED_REGEX)) {
333                    r.myRegex = DCCppConstants.CURRENT_REPLY_NAMED_REGEX;
334                } else if (s.matches(DCCppConstants.CURRENT_REPLY_REGEX)) {
335                    r.myRegex = DCCppConstants.CURRENT_REPLY_REGEX;
336                }
337                return(r);
338            case DCCppConstants.METER_REPLY:
339                if (s.matches(DCCppConstants.METER_REPLY_REGEX)) {
340                    r.myRegex = DCCppConstants.METER_REPLY_REGEX;
341                }
342                return(r);
343            case DCCppConstants.MAXNUMSLOTS_REPLY:
344                if (s.matches(DCCppConstants.MAXNUMSLOTS_REPLY_REGEX)) {
345                    r.myRegex = DCCppConstants.MAXNUMSLOTS_REPLY_REGEX;
346                }
347                return(r);
348            case DCCppConstants.DIAG_REPLY:
349                if (s.matches(DCCppConstants.DIAG_REPLY_REGEX)) {
350                    r.myRegex = DCCppConstants.DIAG_REPLY_REGEX;
351                }
352                return(r);
353            case DCCppConstants.WRITE_EEPROM_REPLY:
354                if (s.matches(DCCppConstants.WRITE_EEPROM_REPLY_REGEX)) {
355                    r.myRegex = DCCppConstants.WRITE_EEPROM_REPLY_REGEX;
356                }
357                return(r);
358            case DCCppConstants.SENSOR_REPLY_H:
359                if (s.matches(DCCppConstants.SENSOR_INACTIVE_REPLY_REGEX)) {
360                    r.myRegex = DCCppConstants.SENSOR_INACTIVE_REPLY_REGEX;
361                }
362                return(r);
363            case DCCppConstants.SENSOR_REPLY_L:
364                if (s.matches(DCCppConstants.SENSOR_DEF_REPLY_REGEX)) {
365                    r.myRegex = DCCppConstants.SENSOR_DEF_REPLY_REGEX;
366                } else if (s.matches(DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX)) {
367                    r.myRegex = DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX;
368                }
369                return(r);
370            case DCCppConstants.MADC_FAIL_REPLY:
371                r.myRegex = DCCppConstants.MADC_FAIL_REPLY_REGEX;
372                return(r);
373            case DCCppConstants.MADC_SUCCESS_REPLY:
374                r.myRegex = DCCppConstants.MADC_SUCCESS_REPLY_REGEX;
375                return(r);
376            case DCCppConstants.COMM_TYPE_REPLY:
377                r.myRegex = DCCppConstants.COMM_TYPE_REPLY_REGEX;
378                return(r);
379            default:
380                return(r);
381        }
382    }
383
384    /**
385     * 
386     * Not really used inside of DCC++.  Just here
387     * to play nicely with the inheritance.
388     * 
389     * TODO: If this is unused, can we just not override it
390     * and (not) "use" the superclass version?
391     * ANSWER: No, we can't because the superclass looks in
392     * the _datachars element, which we don't use, and which
393     * will contain garbage data.  Better to return something
394     * meaningful.
395     * @return first char of myReply as integer
396     */
397    @Override
398    public int getOpCode() {
399        if (myReply.length() > 0) {
400            return(Character.getNumericValue(myReply.charAt(0)));
401        } else {
402            return(0);
403        }
404    }
405
406    /** Get the opcode as a one character string.
407     * @return first char of myReply
408     */
409    public char getOpCodeChar() {
410        if (myReply.length() > 0) {
411            return(myReply.charAt(0));
412        } else {
413            return(0);
414        }
415// return ((char)(getElement(0) & 0x00FF));
416    }
417
418    @Override
419    public int getElement(int n) {
420        if ((n >= 0) && (n < myReply.length())) {
421            return(myReply.charAt(n));
422        } else {
423            return(' ');
424        }
425    }
426
427    @Override
428    public void setElement(int n, int v) {
429        // We want the ASCII value, not the string interpretation of the int
430        char c = (char)(v & 0xFF);
431        if (myReply == null) {
432            myReply = new StringBuilder(Character.toString(c));
433        } else if (n >= myReply.length()) {
434            myReply.append(c);
435        } else if (n > 0) {
436            myReply.setCharAt(n,c);
437        }
438    }
439    /**
440     * Get an integer representation of a BCD value
441     *
442     * @param n byte in message to convert
443     * @return Integer value of BCD byte.
444     */
445    // Not sure how (or if) useful in DCC++
446    @Deprecated
447    public Integer getElementBCD(int n) {
448        return Integer.decode(Integer.toHexString(getElement(n)));
449    }
450
451    public boolean getValueBool(int idx) {
452        Matcher m = DCCppReply.match(myReply.toString(), myRegex, "gvb");
453        if (m == null) {
454            log.error("DCCppReply '{}' not matched by '{}'", this.toString(), myRegex);
455            return(false);
456        } else if (idx <= m.groupCount()) {
457            return(!m.group(idx).equals("0"));
458        } else {
459            log.error("DCCppReply bool value index too big. idx = {} msg = {}", idx, this.toString());
460            return(false);
461        }
462    }
463
464    public String getValueString(int idx) {
465        Matcher m = DCCppReply.match(myReply.toString(), myRegex, "gvs");
466        if (m == null) {
467            log.error("DCCppReply '{}' not matched by '{}'", this.toString(), myRegex);
468            return("");
469        } else if (idx <= m.groupCount()) {
470            return(m.group(idx));
471        } else {
472            log.error("DCCppReply string value index too big. idx = {} msg = {}", idx, this.toString());
473            return("");
474        }
475    }
476
477    //is there a match at idx?
478    public boolean valueExists(int idx) {
479        Matcher m = DCCppReply.match(myReply.toString(), myRegex, "gvs");
480        return (m != null) && (idx <= m.groupCount());
481    }
482
483    public int getValueInt(int idx) {
484        Matcher m = DCCppReply.match(myReply.toString(), myRegex, "gvi");
485        if (m == null) {
486            log.error("DCCppReply '{}' not matched by '{}'", this.toString(), myRegex);
487            return(0);
488        } else if (idx <= m.groupCount()) {
489            return(Integer.parseInt(m.group(idx)));
490        } else {
491            log.error("DCCppReply int value index too big. idx = {} msg = {}", idx, this.toString());
492            return(0);
493        }
494    }
495
496    public double getValueDouble(int idx) {
497        Matcher m = DCCppReply.match(myReply.toString(), myRegex, "gvd");
498        if (m == null) {
499            log.error("DCCppReply '{}' not matched by '{}'", this.toString(), myRegex);
500            return(0.0);
501        } else if (idx <= m.groupCount()) {
502            return(Double.parseDouble(m.group(idx)));
503        } else {
504            log.error("DCCppReply double value index too big. idx = {} msg = {}", idx, this.toString());
505            return(0.0);
506        }
507    }
508
509    /*
510     * skipPrefix is not used at this point in time, but is
511     *  defined as abstract in AbstractMRReply
512     */
513    @Override
514    protected int skipPrefix(int index) {
515        return -1;
516    }
517
518    @Override
519    public int maxSize() {
520        return DCCppConstants.MAX_REPLY_SIZE;
521    }
522
523    public int getLength() {
524        return(myReply.length());
525    }
526
527    /* Some notes on DCC++ messages and responses...
528     *
529     * Messages that have responses expected:
530     * t : <T REGISTER SPEED DIRECTION>
531     * f : (none)
532     * a : (none)
533     * T : <H ID THROW>
534     * w : (none)
535     * b : (none)
536     * W : <r CALLBACKNUM|CALLBACKSUB|CV CV_Value>
537     * B : <r CALLBACKNUM CALLBACKSUB|CV|Bit CV_Bit_Value>
538     * R : <r CALLBACKNUM|CALLBACKSUB|CV CV_Value>
539     * 1 : <p1>
540     * 0 : <p0>
541     * c : <a CURRENT>
542     * s : Series of status messages...
543     *     <p[0,1]>  Power state
544     *     <T ...>Throttle responses from all registers
545     *     <iDCC++ ... > Base station version and build date
546     *     <H ID ADDR INDEX THROW> All turnout states.
547     *
548     * Unsolicited Replies
549     *   | <Q snum [0,1]> Sensor reply.
550     * Debug messages:
551     * M : (none)
552     * P : (none)
553     * f : <f MEM>
554     * L : <M ... data ... >
555     */
556
557    //-------------------------------------------------------------------
558    // Message helper functions
559    // Core methods
560
561    protected boolean matches(String pat) {
562        return(match(this.toString(), pat, "Validator") != null);
563    }
564
565    protected static Matcher match(String s, String pat, String name) {
566        try {
567            Pattern p = Pattern.compile(pat);
568            Matcher m = p.matcher(s);
569            if (!m.matches()) {
570                log.trace("No Match {} Command: {} pattern {}",name, s, pat);
571                return(null);
572            }
573            return(m);
574
575        } catch (PatternSyntaxException e) {
576            log.error("Malformed DCC++ reply syntax! s = {}", pat);
577            return(null);
578        } catch (IllegalStateException e) {
579            log.error("Group called before match operation executed string = {}", s);
580            return(null);
581        } catch (IndexOutOfBoundsException e) {
582            log.error("Index out of bounds string = {}", s);
583            return(null);
584        }
585    }
586
587    public String getStationType() {
588        if (this.isStatusReply()) {
589            return(this.getValueString(1)); //1st match in all versions
590        } else {
591            return("Unknown");
592        }
593    }
594
595    //build value is 3rd match in v3+, 2nd in previous 
596    public String getBuildString() {
597        if (this.isStatusReply()) {
598            if (this.valueExists(3)) {             
599                return(this.getValueString(3));
600            } else {
601                return(this.getValueString(2));
602            }
603        } else {
604            return("Unknown");
605        }
606    }
607
608    //look for canonical version in 2nd match 
609    public String getVersion() {
610        if (this.isStatusReply()) {
611            String s = this.getValueString(2);   
612            if (jmri.Version.isCanonicalVersion(s)) {
613                return s;
614            } else {
615                return("0.0.0");
616            }
617        } else {
618            return("Unknown");
619        }
620    }
621
622     //------------------------------------------------------
623    // Helper methods for ThrottleReplies
624
625    public String getRegisterString() {
626        if (this.isThrottleReply()) {
627            return(this.getValueString(1));
628        } else {
629            log.error("ThrottleReply Parser called on non-Throttle message type {}", this.getOpCodeChar());
630            return("0");
631        }
632    }
633
634    public int getRegisterInt() {
635        if (this.isThrottleReply()) {
636            return(this.getValueInt(1));
637        } else {
638            log.error("ThrottleReply Parser called on non-Throttle message type {}", this.getOpCodeChar());
639            return(0);
640        }
641    }
642
643    public String getSpeedString() {
644        if (this.isThrottleReply()) {
645            return(this.getValueString(2));
646            } else {
647                log.error("ThrottleReply Parser called on non-Throttle message type {}", this.getOpCodeChar());
648                return("0");
649        }
650    }
651
652    public int getSpeedInt() {
653        if (this.isThrottleReply()) {
654            return(this.getValueInt(2));
655        } else {
656            log.error("ThrottleReply Parser called on non-Throttle message type {}", this.getOpCodeChar());
657            return(0);
658        }
659    }
660
661    public String getDirectionString() {
662        // Will return "Forward" (true) or "Reverse" (false)
663        if (this.isThrottleReply()) {
664            return(this.getValueBool(3) ? "Forward" : "Reverse");
665        } else {
666            log.error("ThrottleReply Parser called on non-ThrottleReply message type {}", this.getOpCodeChar());
667            return("Not a Throttle");
668        }
669    }
670
671    public int getDirectionInt() {
672        // Will return 1 (true) or 0 (false)
673        if (this.isThrottleReply()) {
674            return(this.getValueInt(3));
675        } else {
676            log.error("ThrottleReply Parser called on non-ThrottleReply message type {}", this.getOpCodeChar());
677            return(0);
678        }
679    }
680
681    public boolean getDirectionBool() {
682        // Will return true or false
683        if (this.isThrottleReply()) {
684            return(this.getValueBool(3));
685        } else {
686            log.error("ThrottleReply Parser called on non-ThrottleReply message type {}", this.getOpCodeChar());
687            return(false);
688        }
689    }
690
691     //------------------------------------------------------
692    // Helper methods for Turnout Replies
693
694    public String getTOIDString() {
695        if (this.isTurnoutReply()) {
696            return(this.getValueString(1));
697        } else {
698            log.error("TurnoutReply Parser called on non-TurnoutReply message type {}", this.getOpCodeChar());
699            return("0");
700        }
701    }
702
703    public int getTOIDInt() {
704        if (this.isTurnoutReply()) {
705            return(this.getValueInt(1));
706        } else {
707            log.error("TurnoutReply Parser called on non-TurnoutReply message type {}", this.getOpCodeChar());
708            return(0);
709        }
710    }
711
712    public String getTOAddressString() {
713        if (this.isTurnoutDefReply()) {
714            return(this.getValueString(2));
715        } else {
716            return("-1");
717        }
718    }
719
720    public int getTOAddressInt() {
721        if (this.isTurnoutDefReply()) {
722            return(this.getValueInt(2));
723        } else {
724            return(-1);
725        }
726    }
727
728    public String getTOAddressIndexString() {
729        if (this.isTurnoutDefReply()) {
730            return(this.getValueString(3));
731        } else {
732            return("-1");
733        }
734    }
735
736    public int getTOAddressIndexInt() {
737        if (this.isTurnoutDefReply()) {
738            return(this.getValueInt(3));
739        } else {
740            return(-1);
741        }
742    }
743
744    public String getTOStateString() {
745        // Will return human readable state. To get string value for command, use
746        // getTOStateInt().toString()
747        if (this.isTurnoutReply()) {
748            return(this.getTOStateInt() == 1 ? "THROWN" : "CLOSED");
749        } else {
750            log.error("TurnoutReply Parser called on non-TurnoutReply message type {}", this.getOpCodeChar());
751            return("Not a Turnout");
752        }
753    }
754
755    public int getTOStateInt() {
756        // Will return 1 (true - thrown) or 0 (false - closed)
757        if (this.isTurnoutDefReply()) { // turnout list response
758            return(this.getValueInt(4));
759        } else if (this.isTurnoutReply()) { // single turnout response
760            return(this.getValueInt(2));
761        } else {
762            log.error("TurnoutReply Parser called on non-TurnoutReply message type {}", this.getOpCodeChar());
763            return(0);
764        }
765    }
766
767    public boolean getTOIsThrown() {
768        return(this.getValueBool(2));
769    }
770
771    public boolean getTOIsClosed() {
772        return(!this.getValueBool(2));
773    }
774
775
776    //------------------------------------------------------
777    // Helper methods for Program Replies
778
779    public String getCallbackNumString() {
780        if (this.isProgramReply() || isProgramBitReply() ) {
781            return(this.getValueString(1));
782        } else {
783            log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar());
784            return("0");
785        }
786    }
787
788    public int getCallbackNumInt() {
789        if (this.isProgramReply() || isProgramBitReply() ) {
790            return(this.getValueInt(1));
791        } else {
792            log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar());
793            return(0);
794        }
795    }
796
797    public String getCallbackSubString() {
798        if (this.isProgramReply() || isProgramBitReply() ) {
799            return(this.getValueString(2));
800        } else {
801            log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar());
802            return("0");
803        }
804    }
805
806    public int getCallbackSubInt() {
807        if (this.isProgramReply() || isProgramBitReply() ) {
808            return(this.getValueInt(2));
809        } else {
810            log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar());
811            return(0);
812        }
813    }
814
815    public String getCVString() {
816        if (this.isProgramReply() || isProgramBitReply() ) {
817            return(this.getValueString(3));
818        } else if (this.isVerifyReply() ) {
819            return(this.getValueString(1));            
820        } else {
821            log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar());
822            return("0");
823        }
824    }
825
826    public int getCVInt() {
827        if (this.isProgramReply() || isProgramBitReply() ) {
828            return(this.getValueInt(3));
829        } else if (this.isVerifyReply() ) {
830            return(this.getValueInt(1));            
831        } else {
832            log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar());
833            return(0);
834        }
835    }
836
837    public String getProgramBitString() {
838        if (this.isProgramBitReply()) {
839            return(this.getValueString(4));
840        } else {
841            log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar());
842            return("0");
843        }
844    }
845
846    public int getProgramBitInt() {
847        if (this.isProgramBitReply()) {
848            return(this.getValueInt(4));
849        } else {
850            log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar());
851            return(0);
852        }
853    }
854
855    public String getReadValueString() {
856        if (this.isProgramReply() || isProgramBitReply() ) {
857            if (this.matches(DCCppConstants.PROGRAM_BIT_REPLY_REGEX)) {
858                return(this.getValueString(5));
859            } else {
860                return(this.getValueString(4));
861            }
862        } else if (this.isVerifyReply() ) {
863            return(this.getValueString(2));            
864        } else {
865            log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar());
866            return("0");
867        }
868    }
869
870    public int getReadValueInt() {
871        return(Integer.parseInt(this.getReadValueString()));
872    }
873
874    public String getPowerDistrictName() {
875        if (this.isNamedPowerReply()) {
876            return(this.getValueString(2));
877        } else {
878            log.error("NamedPowerReply Parser called on non-NamedPowerReply message type '{}' message '{}'", this.getOpCodeChar(), this.toString());
879            return("");
880        }
881    }
882
883    public String getPowerDistrictStatus() {
884        if (this.isNamedPowerReply()) {
885           if(this.getValueString(1).equals(DCCppConstants.POWER_OFF)) {
886               return("OFF");
887           } else if(this.getValueString(1).equals(DCCppConstants.POWER_ON)) {
888               return("ON");
889           } else {
890               return("OVERLOAD");
891           }
892        } else {
893            log.error("NamedPowerReply Parser called on non-NamedPowerReply message type {} message {}", this.getOpCodeChar(), this.toString());
894            return("");
895        }
896    }
897
898    public String getCurrentString() {
899        if (this.isCurrentReply()) {
900            if(this.isNamedCurrentReply()) {
901                return(this.getValueString(2));
902            }
903            return(this.getValueString(1));
904        } else {
905            log.error("CurrentReply Parser called on non-CurrentReply message type {} message {}", this.getOpCodeChar(), this.toString());
906            return("0");
907        }
908    }
909
910    public int getCurrentInt() {
911        return(Integer.parseInt(this.getCurrentString()));
912    }
913
914    public String getMeterName() {
915        if (this.isMeterReply()) {
916            return(this.getValueString(1));
917        } else {
918            log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), this.toString());
919            return("");
920        }
921    }
922    public double getMeterValue() {
923        if (this.isMeterReply()) {
924            return(this.getValueDouble(2));
925        } else {
926            log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), this.toString());
927            return(0.0);
928        }
929    }
930    public String getMeterType() {
931        if (this.isMeterReply()) {
932            String t = getValueString(3);
933            if (t.equals(DCCppConstants.VOLTAGE) || t.equals(DCCppConstants.CURRENT)) {
934                return(t);                
935            } else {
936                log.warn("Meter Type '{}' is not valid type in message '{}'", t, this.toString());
937                return("");                
938            }
939        } else {
940            log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), this.toString());
941            return("");
942        }
943    }
944    public jmri.Meter.Unit getMeterUnit() {
945        if (this.isMeterReply()) {
946            String us = this.getValueString(4);
947            AbstractXmlAdapter.EnumIO<jmri.Meter.Unit> map = new AbstractXmlAdapter.EnumIoNames<>(jmri.Meter.Unit.class);
948            return(map.inputFromString(us));
949        } else {
950            log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), this.toString());
951            return(jmri.Meter.Unit.NoPrefix);
952        }
953    }
954    public double getMeterMinValue() {
955        if (this.isMeterReply()) {
956            return(this.getValueDouble(5));
957        } else {
958            log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), this.toString());
959            return(0.0);
960        }
961    }
962    public double getMeterMaxValue() {
963        if (this.isMeterReply()) {
964            return(this.getValueDouble(6));
965        } else {
966            log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), this.toString());
967            return(0.0);
968        }
969    }
970    public double getMeterResolution() {
971        if (this.isMeterReply()) {
972            return(this.getValueDouble(7));
973        } else {
974            log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), this.toString());
975            return(0.0);
976        }
977    }
978    public double getMeterWarnValue() {
979        if (this.isMeterReply()) {
980            return(this.getValueDouble(8));
981        } else {
982            log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), this.toString());
983            return(0.0);
984        }
985    }
986    public boolean isMeterTypeVolt() {
987        if (this.isMeterReply()) {
988            return(this.getMeterType().equals(DCCppConstants.VOLTAGE));
989        } else {
990            log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), this.toString());
991            return(false);
992        }
993    }
994    public boolean isMeterTypeCurrent() {
995        if (this.isMeterReply()) {
996            return(this.getMeterType().equals(DCCppConstants.CURRENT));
997        } else {
998            log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), this.toString());
999            return(false);
1000        }
1001    }
1002
1003    public boolean getPowerBool() {
1004        if (this.isPowerReply()) {
1005            return(this.getValueString(1).equals(DCCppConstants.POWER_ON));
1006        } else {
1007            log.error("PowerReply Parser called on non-PowerReply message type {} message {}", this.getOpCodeChar(), this.toString());
1008            return(false);
1009        }
1010    }
1011
1012    public String getTurnoutDefNumString() {
1013        if (this.isTurnoutDefReply()) {
1014            return(this.getValueString(1));
1015        } else {
1016            log.error("TurnoutDefReply Parser called on non-TurnoutDefReply message type {}", this.getOpCodeChar());
1017            return("0");
1018        }
1019    }
1020
1021    public int getTurnoutDefNumInt() {
1022        return(Integer.parseInt(this.getTurnoutDefNumString()));
1023    }
1024
1025    public String getTurnoutDefAddrString() {
1026        if (this.isTurnoutDefReply()) {
1027            return(this.getValueString(2));
1028        } else {
1029            log.error("TurnoutDefReply Parser called on non-TurnoutDefReply message type {}", this.getOpCodeChar());
1030            return("0");
1031        }
1032    }
1033
1034    public int getTurnoutDefAddrInt() {
1035        return(Integer.parseInt(this.getTurnoutDefAddrString()));
1036    }
1037
1038    public String getTurnoutDefSubAddrString() {
1039        if (this.isTurnoutDefReply()) {
1040            return(this.getValueString(3));
1041        } else {
1042            log.error("TurnoutDefReply Parser called on non-TurnoutDefReply message type {}", this.getOpCodeChar());
1043            return("0");
1044        }
1045    }
1046
1047    public int getTurnoutDefSubAddrInt() {
1048        return(Integer.parseInt(this.getTurnoutDefSubAddrString()));
1049    }
1050
1051
1052    public String getOutputNumString() {
1053        if (this.isOutputListReply() || this.isOutputCmdReply()) {
1054            return(this.getValueString(1));
1055        } else {
1056            log.error("OutputAddReply Parser called on non-OutputAddReply message type {}", this.getOpCodeChar());
1057            return("0");
1058        }
1059    }
1060
1061    public int getOutputNumInt() {
1062        return(Integer.parseInt(this.getOutputNumString()));
1063    }
1064
1065    public String getOutputListPinString() {
1066        if (this.isOutputListReply()) {
1067            return(this.getValueString(2));
1068        } else {
1069            log.error("OutputAddReply Parser called on non-OutputAddReply message type {}", this.getOpCodeChar());
1070            return("0");
1071        }
1072    }
1073
1074    public int getOutputListPinInt() {
1075        return(Integer.parseInt(this.getOutputListPinString()));
1076    }
1077
1078    public String getOutputListIFlagString() {
1079        if (this.isOutputListReply()) {
1080            return(this.getValueString(3));
1081        } else {
1082            log.error("OutputListReply Parser called on non-OutputListReply message type {}", this.getOpCodeChar());
1083            return("0");
1084        }
1085    }
1086
1087    public int getOutputListIFlagInt() {
1088        return(Integer.parseInt(this.getOutputListIFlagString()));
1089    }
1090
1091    public String getOutputListStateString() {
1092        if (this.isOutputListReply()) {
1093            return(this.getValueString(4));
1094        } else {
1095            log.error("OutputListReply Parser called on non-OutputListReply message type {}", this.getOpCodeChar());
1096            return("0");
1097        }
1098    }
1099
1100    public int getOutputListStateInt() {
1101        return(Integer.parseInt(this.getOutputListStateString()));
1102    }
1103
1104    public boolean getOutputCmdStateBool() {
1105        if (this.isOutputCmdReply()) {
1106            return((this.getValueBool(2)));
1107        } else {
1108            log.error("OutputCmdReply Parser called on non-OutputCmdReply message type {}", this.getOpCodeChar());
1109            return(false);
1110        }
1111    }
1112
1113    public String getOutputCmdStateString() {
1114        if (this.isOutputCmdReply()) {
1115            return(this.getValueBool(2) ? "HIGH" : "LOW");
1116        } else {
1117            log.error("OutputCmdReply Parser called on non-OutputCmdReply message type {}", this.getOpCodeChar());
1118            return("0");
1119        }
1120    }
1121
1122    public int getOutputCmdStateInt() {
1123        return(this.getOutputCmdStateBool() ? 1 : 0);
1124    }
1125
1126    public boolean getOutputIsHigh() {
1127        return(this.getOutputCmdStateBool());
1128    }
1129
1130    public boolean getOutputIsLow() {
1131        return(!this.getOutputCmdStateBool());
1132    }
1133
1134    public String getSensorDefNumString() {
1135        if (this.isSensorDefReply()) {
1136            return(this.getValueString(1));
1137        } else {
1138            log.error("SensorDefReply Parser called on non-SensorDefReply message type {}", this.getOpCodeChar());
1139            return("0");
1140        }
1141    }
1142
1143    public int getSensorDefNumInt() {
1144        return(Integer.parseInt(this.getSensorDefNumString()));
1145    }
1146
1147    public String getSensorDefPinString() {
1148        if (this.isSensorDefReply()) {
1149            return(this.getValueString(2));
1150        } else {
1151            log.error("SensorDefReply Parser called on non-SensorDefReply message type {}", this.getOpCodeChar());
1152            return("0");
1153        }
1154    }
1155
1156    public int getSensorDefPinInt() {
1157        return(Integer.parseInt(this.getSensorDefPinString()));
1158    }
1159
1160    public String getSensorDefPullupString() {
1161        if (this.isSensorDefReply()) {
1162            return(this.getSensorDefPullupBool() ? "Pullup" : "NoPullup");
1163        } else {
1164            log.error("SensorDefReply Parser called on non-SensorDefReply message type {}", this.getOpCodeChar());
1165            return("Not a Sensor");
1166        }
1167    }
1168
1169    public int getSensorDefPullupInt() {
1170        if (this.isSensorDefReply()) {
1171            return(this.getValueInt(3));
1172        } else {
1173            log.error("SensorDefReply Parser called on non-SensorDefReply message type {}", this.getOpCodeChar());
1174            return(0);
1175        }
1176    }
1177    public boolean getSensorDefPullupBool() {
1178        if (this.isSensorDefReply()) {
1179            return(this.getValueBool(3));
1180        } else {
1181            log.error("SensorDefReply Parser called on non-SensorDefReply message type {}", this.getOpCodeChar());
1182            return(false);
1183        }
1184    }
1185
1186    public String getSensorNumString() {
1187        if (this.isSensorReply()) {
1188            return(this.getValueString(1));
1189        } else {
1190            log.error("SensorReply Parser called on non-SensorReply message type {}", this.getOpCodeChar());
1191            return("0");
1192        }
1193    }
1194
1195    public int getSensorNumInt() {
1196        return(Integer.parseInt(this.getSensorNumString()));
1197    }
1198
1199    public String getSensorStateString() {
1200        if (this.isSensorReply()) {
1201            return(this.myRegex.equals(DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX) ? "Active" : "Inactive");
1202        } else {
1203            log.error("SensorReply Parser called on non-SensorReply message type {}", this.getOpCodeChar());
1204            return("Not a Sensor");
1205        }
1206    }
1207
1208    public int getSensorStateInt() {
1209        if (this.isSensorReply()) {
1210            return(this.myRegex.equals(DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX) ? 1 : 0);
1211        } else {
1212            log.error("SensorReply Parser called on non-SensorReply message type {}", this.getOpCodeChar());
1213        return(0);
1214        }
1215    }
1216
1217    public boolean getSensorIsActive() {
1218        return(this.myRegex.equals(DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX));
1219    }
1220
1221    public boolean getSensorIsInactive() {
1222        return(this.myRegex.equals(DCCppConstants.SENSOR_INACTIVE_REPLY_REGEX));
1223    }
1224
1225    public int getCommTypeInt() {
1226        if (this.isCommTypeReply()) {
1227            return(this.getValueInt(1));
1228        } else {
1229            log.error("CommTypeReply Parser called on non-CommTypeReply message type {}", this.getOpCodeChar());
1230            return(0);
1231        }
1232    }
1233    
1234    public String getCommTypeValueString() {
1235        if (this.isCommTypeReply()) {
1236            return(this.getValueString(2));
1237        } else {
1238            log.error("CommTypeReply Parser called on non-CommTypeReply message type {}", this.getOpCodeChar());
1239            return("N/A");
1240        }
1241    }
1242
1243
1244    //-------------------------------------------------------------------
1245
1246
1247     // Message Identification functions
1248    public boolean isThrottleReply() { return (this.getOpCodeChar() == DCCppConstants.THROTTLE_REPLY); }
1249    public boolean isTurnoutReply() { return (this.getOpCodeChar() == DCCppConstants.TURNOUT_REPLY); }
1250    public boolean isProgramReply() { return (this.matches(DCCppConstants.PROGRAM_REPLY_REGEX)); }
1251    public boolean isVerifyReply()  { return (this.matches(DCCppConstants.PROGRAM_VERIFY_REGEX)); }
1252    public boolean isProgramBitReply() { return (this.matches(DCCppConstants.PROGRAM_BIT_REPLY_REGEX)); }
1253    public boolean isPowerReply() { return (this.getOpCodeChar() == DCCppConstants.POWER_REPLY); }
1254    public boolean isNamedPowerReply() { return(this.matches(DCCppConstants.TRACK_POWER_REPLY_NAMED_REGEX)); }
1255    public boolean isMaxNumSlotsReply() { return(this.matches(DCCppConstants.MAXNUMSLOTS_REPLY_REGEX)); }
1256    public boolean isDiagReply() { return(this.matches(DCCppConstants.DIAG_REPLY_REGEX)); }
1257    public boolean isCurrentReply() { return (this.getOpCodeChar() == DCCppConstants.CURRENT_REPLY); }
1258    public boolean isNamedCurrentReply() { return(this.matches(DCCppConstants.CURRENT_REPLY_NAMED_REGEX)); }
1259    public boolean isMeterReply()   { return (this.matches(DCCppConstants.METER_REPLY_REGEX)); }
1260    public boolean isSensorReply() { return((this.getOpCodeChar() == DCCppConstants.SENSOR_REPLY) ||
1261         (this.getOpCodeChar() == DCCppConstants.SENSOR_REPLY_H) ||
1262         (this.getOpCodeChar() == DCCppConstants.SENSOR_REPLY_L)); }
1263    public boolean isSensorDefReply() { return(this.matches(DCCppConstants.SENSOR_DEF_REPLY_REGEX)); }
1264    public boolean isTurnoutDefReply() { return(this.matches(DCCppConstants.TURNOUT_DEF_REPLY_REGEX)); }
1265    public boolean isMADCFailReply() { return(this.getOpCodeChar() == DCCppConstants.MADC_FAIL_REPLY); }
1266    public boolean isMADCSuccessReply() { return(this.getOpCodeChar() == DCCppConstants.MADC_SUCCESS_REPLY); }
1267    public boolean isStatusReply() { return(this.getOpCodeChar() == DCCppConstants.STATUS_REPLY); }
1268    public boolean isOutputListReply() { return(this.matches(DCCppConstants.OUTPUT_LIST_REPLY_REGEX)); }
1269    public boolean isOutputCmdReply() { return(this.matches(DCCppConstants.OUTPUT_REPLY_REGEX)); }
1270    public boolean isCommTypeReply() { return(this.matches(DCCppConstants.COMM_TYPE_REPLY_REGEX)); }
1271    public boolean isWriteEepromReply() { return(this.matches(DCCppConstants.WRITE_EEPROM_REPLY_REGEX)); }
1272
1273    public boolean isValidReplyFormat() {
1274        if ((this.matches(DCCppConstants.THROTTLE_REPLY_REGEX)) ||
1275            (this.matches(DCCppConstants.TURNOUT_REPLY_REGEX)) ||
1276            (this.matches(DCCppConstants.PROGRAM_REPLY_REGEX)) ||
1277            (this.matches(DCCppConstants.PROGRAM_VERIFY_REGEX)) ||
1278            (this.matches(DCCppConstants.TRACK_POWER_REPLY_REGEX)) ||
1279            (this.matches(DCCppConstants.TRACK_POWER_REPLY_NAMED_REGEX)) ||
1280            (this.matches(DCCppConstants.CURRENT_REPLY_REGEX)) ||
1281            (this.matches(DCCppConstants.CURRENT_REPLY_NAMED_REGEX)) ||
1282            (this.matches(DCCppConstants.METER_REPLY_REGEX)) ||
1283            (this.matches(DCCppConstants.SENSOR_REPLY_REGEX)) ||
1284            (this.matches(DCCppConstants.SENSOR_DEF_REPLY_REGEX)) ||
1285            (this.matches(DCCppConstants.SENSOR_INACTIVE_REPLY_REGEX)) ||
1286            (this.matches(DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX)) ||
1287            (this.matches(DCCppConstants.OUTPUT_REPLY_REGEX)) ||
1288            (this.matches(DCCppConstants.MADC_FAIL_REPLY_REGEX)) ||
1289            (this.matches(DCCppConstants.MADC_SUCCESS_REPLY_REGEX)) ||
1290            (this.matches(DCCppConstants.STATUS_REPLY_REGEX)) ||
1291            (this.matches(DCCppConstants.STATUS_REPLY_BSC_REGEX)) ||
1292            (this.matches(DCCppConstants.STATUS_REPLY_ESP32_REGEX)) ||
1293            (this.matches(DCCppConstants.STATUS_REPLY_DCCEX_REGEX))
1294        ) {
1295            return(true);
1296        } else {
1297            return(false);
1298        }
1299    }
1300
1301    // initialize logging
1302    private final static Logger log = LoggerFactory.getLogger(DCCppReply.class);
1303
1304}