001package jmri.jmrix.dccpp; 002 003import java.util.LinkedHashMap; 004import java.util.ArrayList; 005import java.util.regex.Matcher; 006import java.util.regex.Pattern; 007import javax.annotation.Nonnull; 008 009import org.apache.commons.lang3.StringUtils; 010import java.util.regex.PatternSyntaxException; 011import org.slf4j.Logger; 012import org.slf4j.LoggerFactory; 013 014import jmri.configurexml.AbstractXmlAdapter; 015 016/** 017 * Represents a single response from the DCC++ system. 018 * 019 * @author Paul Bender Copyright (C) 2004 020 * @author Mark Underwood Copyright (C) 2015 021 * @author Harald Barth Copyright (C) 2019 022 * 023 * Based on XNetReply 024 */ 025 026/* 027 * A few notes on implementation 028 * 029 * DCCppReply objects are (usually) created by parsing a String that is the 030 * result of an incoming reply message from the Base Station. The information is 031 * stored as a String, along with a Regex string that allows the individual data 032 * elements to be extracted when needed. 033 * 034 * Listeners and other higher level code should first check to make sure the 035 * DCCppReply is of the correct type by calling the relevant isMessageType() 036 * method. Then, call the various getThisDataElement() method to retrieve the 037 * data of interest. 038 * 039 * For example, to get the Speed from a Throttle Reply, first check that it /is/ 040 * a ThrottleReply by calling message.isThrottleReply(), and then get the speed 041 * by calling message.getSpeedInt() or getSpeedString(). 042 * 043 * The reason for all of this misdirection is to make sure that the upper layer 044 * JMRI code is isolated/insulated from any changes in the actual Base Station 045 * message format. For example, there is no need for the listener code to know 046 * that the speed is the second number after the "T" in the reply (nor that a 047 * Throttle reply starts with a "T"). 048 */ 049 050public class DCCppReply extends jmri.jmrix.AbstractMRReply { 051 052 protected String myRegex; 053 protected StringBuilder myReply; 054 055 // Create a new reply. 056 public DCCppReply() { 057 super(); 058 setBinary(false); 059 myRegex = ""; 060 myReply = new StringBuilder(); 061 } 062 063 // Create a new reply from an existing reply 064 public DCCppReply(DCCppReply reply) { 065 super(reply); 066 setBinary(false); 067 myRegex = reply.myRegex; 068 myReply = reply.myReply; 069 } 070 071 // Create a new reply from a string 072 public DCCppReply(String reply) { 073 super(); 074 setBinary(false); 075 myRegex = ""; 076 myReply = new StringBuilder(reply); 077 _nDataChars = reply.length(); 078 } 079 080 /** 081 * Override default toString. 082 * @return myReply StringBuilder toString. 083 */ 084 @Override 085 public String toString() { 086 log.trace("DCCppReply.toString(): msg '{}'", myReply); 087 return myReply.toString(); 088 } 089 090 /** 091 * Generate text translations of replies for use in the DCCpp monitor. 092 * 093 * @return representation of the DCCppReply as a string. 094 **/ 095 @Override 096 public String toMonitorString() { 097 // Beautify and display 098 String text; 099 100 switch (getOpCodeChar()) { 101 case DCCppConstants.THROTTLE_REPLY: 102 text = "Throttle Reply: "; 103 text += "Register: " + getRegisterString() + ", "; 104 text += "Speed: " + getSpeedString() + ", "; 105 text += "Direction: " + getDirectionString(); 106 break; 107 case DCCppConstants.TURNOUT_REPLY: 108 if (isTurnoutCmdReply()) { 109 text = "Turnout Reply: "; 110 text += "ID: " + getTOIDString() + ", "; 111 text += "Dir: " + getTOStateString(); 112 } else if (isTurnoutDefReply()) { 113 text = "Turnout Def Reply: "; 114 text += "ID:" + getTOIDString() + ", "; 115 text += "Address:" + getTOAddressString() + ", "; 116 text += "Index:" + getTOAddressIndexString() + ", "; 117 // if we are able to parse the address and index we can convert it 118 // to a standard DCC address for display. 119 if (getTOAddressInt() != -1 && getTOAddressIndexInt() != -1) { 120 int boardAddr = getTOAddressInt(); 121 int boardIndex = getTOAddressIndexInt(); 122 int dccAddress = (((boardAddr - 1) * 4) + boardIndex) + 1; 123 text += "DCC Address: " + dccAddress + ", "; 124 } 125 text += "Dir: " + getTOStateString(); 126 } else if (isTurnoutDefDCCReply()) { 127 text = "Turnout Def DCC Reply: "; 128 text += "ID:" + getTOIDString() + ", "; 129 text += "Address:" + getTOAddressString() + ", "; 130 text += "Index:" + getTOAddressIndexString() + ", "; 131 // if we are able to parse the address and index we can convert it 132 // to a standard DCC address for display. 133 if (getTOAddressInt() != -1 && getTOAddressIndexInt() != -1) { 134 int boardAddr = getTOAddressInt(); 135 int boardIndex = getTOAddressIndexInt(); 136 int dccAddress = (((boardAddr - 1) * 4) + boardIndex) + 1; 137 text += "DCC Address:" + dccAddress + ", "; 138 } 139 text += "Dir:" + getTOStateString(); 140 } else if (isTurnoutDefServoReply()) { 141 text = "Turnout Def SERVO Reply: "; 142 text += "ID:" + getTOIDString() + ", "; 143 text += "Pin:" + getTOPinInt() + ", "; 144 text += "ThrownPos:" + getTOThrownPositionInt() + ", "; 145 text += "ClosedPos:" + getTOClosedPositionInt() + ", "; 146 text += "Profile:" + getTOProfileInt() + ", "; 147 text += "Dir:" + getTOStateString(); 148 } else if (isTurnoutDefVpinReply()) { 149 text = "Turnout Def VPIN Reply: "; 150 text += "ID:" + getTOIDString() + ", "; 151 text += "Pin:" + getTOPinInt() + ", "; 152 text += "Dir:" + getTOStateString(); 153 } else if (isTurnoutDefLCNReply()) { 154 text = "Turnout Def LCN Reply: "; 155 text += "ID:" + getTOIDString() + ", "; 156 text += "Dir:" + getTOStateString(); 157 } else { 158 text = "Unknown Turnout Reply Format: "; 159 text += toString(); 160 } 161 break; 162 case DCCppConstants.SENSOR_REPLY_H: 163 text = "Sensor Reply (Inactive): "; 164 text += "Number: " + getSensorNumString() + ", "; 165 text += "State: INACTIVE"; 166 break; 167 case DCCppConstants.SENSOR_REPLY_L: 168 // Also covers the V1.0 version SENSOR_REPLY 169 if (isSensorDefReply()) { 170 text = "Sensor Def Reply: "; 171 text += "Number: " + getSensorDefNumString() + ", "; 172 text += "Pin: " + getSensorDefPinString() + ", "; 173 text += "Pullup: " + getSensorDefPullupString(); 174 } else { 175 text = "Sensor Reply (Active): "; 176 text += "Number: " + getSensorNumString() + ", "; 177 text += "State: ACTIVE"; 178 } 179 break; 180 case DCCppConstants.OUTPUT_REPLY: 181 if (isOutputCmdReply()) { 182 text = "Output Command Reply: "; 183 text += "Number: " + getOutputNumString() + ", "; 184 text += "State: " + getOutputCmdStateString(); 185 } else if (isOutputDefReply()) { 186 text = "Output Command Reply: "; 187 text += "Number: " + getOutputNumString() + ", "; 188 text += "Pin: " + getOutputListPinString() + ", "; 189 text += "Flags: " + getOutputListIFlagString() + ", "; 190 text += "State: " + getOutputListStateString(); 191 } else { 192 text = "Invalid Output Reply Format: "; 193 text += toString(); 194 } 195 break; 196 case DCCppConstants.PROGRAM_REPLY: 197 if (isProgramBitReply()) { 198 text = "Program Bit Reply: "; 199 text += "CallbackNum:" + getCallbackNumString() + ", "; 200 text += "Sub:" + getCallbackSubString() + ", "; 201 text += "CV:" + getCVString() + ", "; 202 text += "Bit:" + getProgramBitString() + ", "; 203 text += "Value:" + getReadValueString(); 204 } else if (isProgramReplyV4()) { 205 text = "Program Reply: "; 206 text += "CV:" + getCVString() + ", "; 207 text += "Value:" + getReadValueString(); 208 } else if (isProgramBitReplyV4()) { 209 text = "Program Bit Reply: "; 210 text += "CV:" + getCVString() + ", "; 211 text += "Bit:" + getProgramBitString() + ", "; 212 text += "Value:" + getReadValueString(); 213 } else if (isProgramLocoIdReply()) { 214 text = "Program LocoId Reply: "; 215 text += "LocoId:" + getLocoIdInt(); 216 } else { 217 text = "Program Reply: "; 218 text += "CallbackNum:" + getCallbackNumString() + ", "; 219 text += "Sub:" + getCallbackSubString() + ", "; 220 text += "CV:" + getCVString() + ", "; 221 text += "Value:" + getReadValueString(); 222 } 223 break; 224 case DCCppConstants.VERIFY_REPLY: 225 text = "Prog Verify Reply: "; 226 text += "CV: " + getCVString() + ", "; 227 text += "Value: " + getReadValueString(); 228 break; 229 case DCCppConstants.STATUS_REPLY: 230 text = "Status:"; 231 text += "Station: " + getStationType(); 232 text += ", Build: " + getBuildString(); 233 text += ", Version: " + getVersion(); 234 break; 235 case DCCppConstants.POWER_REPLY: 236 if (isNamedPowerReply()) { 237 text = "Power Status: "; 238 text += "Name:" + getPowerDistrictName(); 239 text += " Status:" + getPowerDistrictStatus(); 240 } else { 241 text = "Power Status: "; 242 text += (getPowerBool() ? "ON" : "OFF"); 243 } 244 break; 245 case DCCppConstants.CURRENT_REPLY: 246 text = "Current: " + getCurrentString() + " / 1024"; 247 break; 248 case DCCppConstants.METER_REPLY: 249 text = String.format( 250 "Meter reply: name %s, value %.2f, type %s, unit %s, min %.2f, max %.2f, resolution %.2f, warn %.2f", 251 getMeterName(), getMeterValue(), getMeterType(), 252 getMeterUnit(), getMeterMinValue(), getMeterMaxValue(), 253 getMeterResolution(), getMeterWarnValue()); 254 break; 255 case DCCppConstants.WRITE_EEPROM_REPLY: 256 text = "Write EEPROM Reply... "; 257 // TODO: Don't use getProgValueString() 258 text += "Turnouts: " + getValueString(1) + ", "; 259 text += "Sensors: " + getValueString(2) + ", "; 260 text += "Outputs: " + getValueString(3); 261 break; 262 case DCCppConstants.COMM_TYPE_REPLY: 263 text = "Comm Type Reply "; 264 text += "Type: " + getCommTypeInt(); 265 text += " Port: " + getCommTypeValueString(); 266 break; 267 case DCCppConstants.MADC_FAIL_REPLY: 268 text = "No Sensor/Turnout/Output Reply "; 269 break; 270 case DCCppConstants.MADC_SUCCESS_REPLY: 271 text = "Sensor/Turnout/Output MADC Success Reply "; 272 break; 273 case DCCppConstants.MAXNUMSLOTS_REPLY: 274 text = "Number of slots reply: " + getValueString(1); 275 break; 276 case DCCppConstants.DIAG_REPLY: 277 text = "DIAG: " + getValueString(1); 278 break; 279 case DCCppConstants.LOCO_STATE_REPLY: 280 text = "Loco State: LocoId:" + getLocoIdInt(); 281 text += " Dir:" + getDirectionString(); 282 text += " Speed:" + getSpeedInt(); 283 text += " F0-28:" + getFunctionsString(); 284 break; 285 case DCCppConstants.THROTTLE_COMMANDS_REPLY: 286 if (isTurnoutIDsReply()) { 287 text = "Turnout IDs:" + getTurnoutIDList(); 288 break; 289 } else if (isTurnoutIDReply()) { 290 text = "Turnout ID:" + getTOIDString(); 291 text += " State:" + getTurnoutStateString(); 292 text += " Desc:'" + getTurnoutDescString() + "'"; 293 break; 294 } else if (isClockReply()) { 295 String hhmm = String.format("%02d:%02d", 296 getClockMinutesInt() / 60, 297 getClockMinutesInt() % 60); 298 text = "FastClock Reply: " + hhmm; 299 if (!getClockRateString().isEmpty()) { 300 text += ", Rate:" + getClockRateString(); 301 } 302 break; 303 } 304 text = "Unknown Message: '" + toString() + "'"; 305 break; 306 case DCCppConstants.TRACKMANAGER_CMD: 307 text = "TrackManager:" + toString(); 308 break; 309 case DCCppConstants.LCD_TEXT_CMD: 310 text = "LCD Text '" + getLCDTextString() + "', disp " + getLCDDisplayNumString() + ", line " + getLCDLineNumString(); 311 break; 312 313 default: 314 text = "Unrecognized reply: '" + toString() + "'"; 315 } 316 317 return text; 318 } 319 320 /** 321 * Generate properties list for certain replies 322 * 323 * @return list of all properties as a string 324 **/ 325 public String getPropertiesAsString() { 326 StringBuilder text = new StringBuilder(); 327 StringBuilder comma = new StringBuilder(); 328 switch (getOpCodeChar()) { 329 case DCCppConstants.TURNOUT_REPLY: 330 case DCCppConstants.SENSOR_REPLY: 331 case DCCppConstants.OUTPUT_REPLY: 332 // write out properties in comment 333 getProperties().forEach((key, value) -> { 334 text.append(comma).append(key).append(":").append(value); 335 comma.setLength(0); 336 comma.append(","); 337 }); 338 339 break; 340 default: 341 break; 342 } 343 return text.toString(); 344 } 345 346 /** 347 * build a propertylist from reply values. 348 * 349 * @return properties hashmap 350 **/ 351 public LinkedHashMap<String, Object> getProperties() { 352 LinkedHashMap<String, Object> properties = new LinkedHashMap<>(); 353 switch (getOpCodeChar()) { 354 case DCCppConstants.TURNOUT_REPLY: 355 if (isTurnoutDefDCCReply()) { 356 properties.put(DCCppConstants.PROP_TYPE, DCCppConstants.TURNOUT_TYPE_DCC); 357 properties.put(DCCppConstants.PROP_ID, getTOIDInt()); 358 properties.put(DCCppConstants.PROP_ADDRESS, getTOAddressInt()); 359 properties.put(DCCppConstants.PROP_INDEX, getTOAddressIndexInt()); 360 // if we are able to parse the address and index we can convert it 361 // to a standard DCC address for display. 362 if (getTOAddressInt() != -1 && getTOAddressIndexInt() != -1) { 363 int boardAddr = getTOAddressInt(); 364 int boardIndex = getTOAddressIndexInt(); 365 int dccAddress = (((boardAddr - 1) * 4) + boardIndex) + 1; 366 properties.put(DCCppConstants.PROP_DCCADDRESS, dccAddress); 367 } 368 } else if (isTurnoutDefServoReply()) { 369 properties.put(DCCppConstants.PROP_TYPE, DCCppConstants.TURNOUT_TYPE_SERVO); 370 properties.put(DCCppConstants.PROP_ID, getTOIDInt()); 371 properties.put(DCCppConstants.PROP_PIN, getTOPinInt()); 372 properties.put(DCCppConstants.PROP_THROWNPOS, getTOThrownPositionInt()); 373 properties.put(DCCppConstants.PROP_CLOSEDPOS, getTOClosedPositionInt()); 374 properties.put(DCCppConstants.PROP_PROFILE, getTOProfileInt()); 375 } else if (isTurnoutDefVpinReply()) { 376 properties.put(DCCppConstants.PROP_TYPE, DCCppConstants.TURNOUT_TYPE_VPIN); 377 properties.put(DCCppConstants.PROP_ID, getTOIDInt()); 378 properties.put(DCCppConstants.PROP_PIN, getTOPinInt()); 379 } else if (isTurnoutDefLCNReply()) { 380 properties.put(DCCppConstants.PROP_TYPE, DCCppConstants.TURNOUT_TYPE_LCN); 381 properties.put(DCCppConstants.PROP_ID, getTOIDInt()); 382 } 383 break; 384 case DCCppConstants.SENSOR_REPLY: 385 if (isSensorDefReply()) { 386 properties.put(DCCppConstants.PROP_TYPE, DCCppConstants.SENSOR_TYPE); 387 properties.put(DCCppConstants.PROP_ID, getSensorDefNumInt()); 388 properties.put(DCCppConstants.PROP_PIN, getSensorDefPinInt()); 389 properties.put(DCCppConstants.PROP_PULLUP, getSensorDefPullupBool()); 390 } 391 break; 392 case DCCppConstants.OUTPUT_REPLY: 393 if (isOutputDefReply()) { 394 properties.put(DCCppConstants.PROP_TYPE, DCCppConstants.OUTPUT_TYPE); 395 properties.put(DCCppConstants.PROP_ID, getOutputNumInt()); 396 properties.put(DCCppConstants.PROP_PIN, getOutputListPinInt()); 397 properties.put(DCCppConstants.PROP_IFLAG, getOutputListIFlagInt()); 398 } 399 break; 400 default: 401 break; 402 } 403 return properties; 404 } 405 406 public void parseReply(String s) { 407 DCCppReply r = DCCppReply.parseDCCppReply(s); 408 log.debug("in parseReply() string: {}", s); 409 if (!(r.toString().isBlank())) { 410 this.myRegex = r.myRegex; 411 this.myReply = r.myReply; 412 this._nDataChars = r._nDataChars; 413 log.trace("copied: this: {}", this); 414 } 415 } 416 417 /// 418 /// 419 /// TODO: Stopped Refactoring to StringBuilder here 12/12/15 420 /// 421 /// 422 423 /** 424 * Parses a string and generates a DCCppReply from the string contents 425 * 426 * @param s String to be parsed 427 * @return DCCppReply or empty string if not a valid formatted string 428 */ 429 @Nonnull 430 public static DCCppReply parseDCCppReply(String s) { 431 432 if (log.isTraceEnabled()) { 433 log.trace("Parse charAt(0): {}", s.charAt(0)); 434 } 435 DCCppReply r = new DCCppReply(s); 436 switch (s.charAt(0)) { 437 case DCCppConstants.STATUS_REPLY: 438 if (s.matches(DCCppConstants.STATUS_REPLY_BSC_REGEX)) { 439 log.debug("BSC Status Reply: '{}'", r); 440 r.myRegex = DCCppConstants.STATUS_REPLY_BSC_REGEX; 441 } else if (s.matches(DCCppConstants.STATUS_REPLY_ESP32_REGEX)) { 442 log.debug("ESP32 Status Reply: '{}'", r); 443 r.myRegex = DCCppConstants.STATUS_REPLY_ESP32_REGEX; 444 } else if (s.matches(DCCppConstants.STATUS_REPLY_REGEX)) { 445 log.debug("Original Status Reply: '{}'", r); 446 r.myRegex = DCCppConstants.STATUS_REPLY_REGEX; 447 } else if (s.matches(DCCppConstants.STATUS_REPLY_DCCEX_REGEX)) { 448 log.debug("DCC-EX Status Reply: '{}'", r); 449 r.myRegex = DCCppConstants.STATUS_REPLY_DCCEX_REGEX; 450 } 451 return (r); 452 case DCCppConstants.THROTTLE_REPLY: 453 if (s.matches(DCCppConstants.THROTTLE_REPLY_REGEX)) { 454 log.debug("Throttle Reply: '{}'", r); 455 r.myRegex = DCCppConstants.THROTTLE_REPLY_REGEX; 456 } 457 return (r); 458 case DCCppConstants.TURNOUT_REPLY: 459 // the order of checking the reply here is critical as both the 460 // TURNOUT_DEF_REPLY and TURNOUT_REPLY regex strings start with 461 // the same strings but have different meanings. 462 if (s.matches(DCCppConstants.TURNOUT_DEF_REPLY_REGEX)) { 463 r.myRegex = DCCppConstants.TURNOUT_DEF_REPLY_REGEX; 464 } else if (s.matches(DCCppConstants.TURNOUT_DEF_DCC_REPLY_REGEX)) { 465 r.myRegex = DCCppConstants.TURNOUT_DEF_DCC_REPLY_REGEX; 466 } else if (s.matches(DCCppConstants.TURNOUT_DEF_SERVO_REPLY_REGEX)) { 467 r.myRegex = DCCppConstants.TURNOUT_DEF_SERVO_REPLY_REGEX; 468 } else if (s.matches(DCCppConstants.TURNOUT_DEF_VPIN_REPLY_REGEX)) { 469 r.myRegex = DCCppConstants.TURNOUT_DEF_VPIN_REPLY_REGEX; 470 } else if (s.matches(DCCppConstants.TURNOUT_DEF_LCN_REPLY_REGEX)) { 471 r.myRegex = DCCppConstants.TURNOUT_DEF_LCN_REPLY_REGEX; 472 } else if (s.matches(DCCppConstants.TURNOUT_REPLY_REGEX)) { 473 r.myRegex = DCCppConstants.TURNOUT_REPLY_REGEX; 474 } else if (s.matches(DCCppConstants.MADC_FAIL_REPLY_REGEX)) { 475 r.myRegex = DCCppConstants.MADC_FAIL_REPLY_REGEX; 476 } 477 log.debug("Parsed Reply: '{}' length {}", r.toString(), r._nDataChars); 478 return (r); 479 case DCCppConstants.OUTPUT_REPLY: 480 if (s.matches(DCCppConstants.OUTPUT_DEF_REPLY_REGEX)) { 481 r.myRegex = DCCppConstants.OUTPUT_DEF_REPLY_REGEX; 482 } else if (s.matches(DCCppConstants.OUTPUT_REPLY_REGEX)) { 483 r.myRegex = DCCppConstants.OUTPUT_REPLY_REGEX; 484 } 485 log.debug("Parsed Reply: '{}' length {}", r, r._nDataChars); 486 return (r); 487 case DCCppConstants.PROGRAM_REPLY: 488 if (s.matches(DCCppConstants.PROGRAM_BIT_REPLY_REGEX)) { 489 log.debug("Matches ProgBitReply"); 490 r.myRegex = DCCppConstants.PROGRAM_BIT_REPLY_REGEX; 491 } else if (s.matches(DCCppConstants.PROGRAM_BIT_REPLY_V4_REGEX)) { 492 log.debug("Matches ProgBitReplyV2"); 493 r.myRegex = DCCppConstants.PROGRAM_BIT_REPLY_V4_REGEX; 494 } else if (s.matches(DCCppConstants.PROGRAM_REPLY_REGEX)) { 495 log.debug("Matches ProgReply"); 496 r.myRegex = DCCppConstants.PROGRAM_REPLY_REGEX; 497 } else if (s.matches(DCCppConstants.PROGRAM_REPLY_V4_REGEX)) { 498 log.debug("Matches ProgReplyV2"); 499 r.myRegex = DCCppConstants.PROGRAM_REPLY_V4_REGEX; 500 } else if (s.matches(DCCppConstants.PROGRAM_LOCOID_REPLY_REGEX)) { 501 log.debug("Matches ProgLocoIDReply"); 502 r.myRegex = DCCppConstants.PROGRAM_LOCOID_REPLY_REGEX; 503 } else { 504 log.debug("Does not match ProgReply Regex"); 505 } 506 return (r); 507 case DCCppConstants.VERIFY_REPLY: 508 if (s.matches(DCCppConstants.PROGRAM_VERIFY_REPLY_REGEX)) { 509 log.debug("Matches VerifyReply"); 510 r.myRegex = DCCppConstants.PROGRAM_VERIFY_REPLY_REGEX; 511 } else { 512 log.debug("Does not match VerifyReply Regex"); 513 } 514 return (r); 515 case DCCppConstants.POWER_REPLY: 516 if (s.matches(DCCppConstants.TRACK_POWER_REPLY_NAMED_REGEX)) { 517 r.myRegex = DCCppConstants.TRACK_POWER_REPLY_NAMED_REGEX; 518 } else if (s.matches(DCCppConstants.TRACK_POWER_REPLY_REGEX)) { 519 r.myRegex = DCCppConstants.TRACK_POWER_REPLY_REGEX; 520 } 521 return (r); 522 case DCCppConstants.CURRENT_REPLY: 523 if (s.matches(DCCppConstants.CURRENT_REPLY_NAMED_REGEX)) { 524 r.myRegex = DCCppConstants.CURRENT_REPLY_NAMED_REGEX; 525 } else if (s.matches(DCCppConstants.CURRENT_REPLY_REGEX)) { 526 r.myRegex = DCCppConstants.CURRENT_REPLY_REGEX; 527 } 528 return (r); 529 case DCCppConstants.METER_REPLY: 530 if (s.matches(DCCppConstants.METER_REPLY_REGEX)) { 531 r.myRegex = DCCppConstants.METER_REPLY_REGEX; 532 } 533 return (r); 534 case DCCppConstants.MAXNUMSLOTS_REPLY: 535 if (s.matches(DCCppConstants.MAXNUMSLOTS_REPLY_REGEX)) { 536 r.myRegex = DCCppConstants.MAXNUMSLOTS_REPLY_REGEX; 537 } 538 return (r); 539 case DCCppConstants.DIAG_REPLY: 540 if (s.matches(DCCppConstants.DIAG_REPLY_REGEX)) { 541 r.myRegex = DCCppConstants.DIAG_REPLY_REGEX; 542 } 543 return (r); 544 case DCCppConstants.LCD_TEXT_REPLY: 545 if (s.matches(DCCppConstants.LCD_TEXT_REPLY_REGEX)) { 546 r.myRegex = DCCppConstants.LCD_TEXT_REPLY_REGEX; 547 } 548 return (r); 549 case DCCppConstants.WRITE_EEPROM_REPLY: 550 if (s.matches(DCCppConstants.WRITE_EEPROM_REPLY_REGEX)) { 551 r.myRegex = DCCppConstants.WRITE_EEPROM_REPLY_REGEX; 552 } 553 return (r); 554 case DCCppConstants.SENSOR_REPLY_H: 555 if (s.matches(DCCppConstants.SENSOR_INACTIVE_REPLY_REGEX)) { 556 r.myRegex = DCCppConstants.SENSOR_INACTIVE_REPLY_REGEX; 557 } 558 return (r); 559 case DCCppConstants.SENSOR_REPLY_L: 560 if (s.matches(DCCppConstants.SENSOR_DEF_REPLY_REGEX)) { 561 r.myRegex = DCCppConstants.SENSOR_DEF_REPLY_REGEX; 562 } else if (s.matches(DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX)) { 563 r.myRegex = DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX; 564 } 565 return (r); 566 case DCCppConstants.MADC_FAIL_REPLY: 567 r.myRegex = DCCppConstants.MADC_FAIL_REPLY_REGEX; 568 return (r); 569 case DCCppConstants.MADC_SUCCESS_REPLY: 570 r.myRegex = DCCppConstants.MADC_SUCCESS_REPLY_REGEX; 571 return (r); 572 case DCCppConstants.COMM_TYPE_REPLY: 573 r.myRegex = DCCppConstants.COMM_TYPE_REPLY_REGEX; 574 return (r); 575 case DCCppConstants.LOCO_STATE_REPLY: 576 r.myRegex = DCCppConstants.LOCO_STATE_REGEX; 577 return (r); 578 case DCCppConstants.THROTTLE_COMMANDS_REPLY: 579 if (s.matches(DCCppConstants.TURNOUT_IDS_REPLY_REGEX)) { 580 r.myRegex = DCCppConstants.TURNOUT_IDS_REPLY_REGEX; 581 } else if (s.matches(DCCppConstants.TURNOUT_ID_REPLY_REGEX)) { 582 r.myRegex = DCCppConstants.TURNOUT_ID_REPLY_REGEX; 583 } else if (s.matches(DCCppConstants.CLOCK_REPLY_REGEX)) { 584 r.myRegex = DCCppConstants.CLOCK_REPLY_REGEX; 585 } 586 log.debug("Parsed Reply: '{}' length {}", r.toString(), r._nDataChars); 587 return (r); 588 case DCCppConstants.TRACKMANAGER_CMD: 589 if (s.matches(DCCppConstants.TRACKMANAGER_REPLY_REGEX)) { 590 r.myRegex = DCCppConstants.TRACKMANAGER_REPLY_REGEX; 591 } 592 return (r); 593 default: 594 return (r); 595 } 596 } 597 598 /** 599 * Not really used inside of DCC++. Just here to play nicely with the 600 * inheritance. 601 * TODO: If this is unused, can we just not override it and (not) "use" 602 * the superclass version? 603 * ANSWER: No, we can't because the superclass looks in the _datachars 604 * element, which we don't use, and which will contain garbage data. 605 * Better to return something meaningful. 606 * 607 * @return first char of myReply as integer 608 */ 609 @Override 610 public int getOpCode() { 611 if (myReply.length() > 0) { 612 return (Character.getNumericValue(myReply.charAt(0))); 613 } else { 614 return (0); 615 } 616 } 617 618 /** 619 * Get the opcode as a one character string. 620 * 621 * @return first char of myReply 622 */ 623 public char getOpCodeChar() { 624 if (myReply.length() > 0) { 625 return (myReply.charAt(0)); 626 } else { 627 return (0); 628 } 629 } 630 631 @Override 632 public int getElement(int n) { 633 if ((n >= 0) && (n < myReply.length())) { 634 return (myReply.charAt(n)); 635 } else { 636 return (' '); 637 } 638 } 639 640 @Override 641 public void setElement(int n, int v) { 642 // We want the ASCII value, not the string interpretation of the int 643 char c = (char) (v & 0xFF); 644 if (myReply == null) { 645 myReply = new StringBuilder(Character.toString(c)); 646 } else if (n >= myReply.length()) { 647 myReply.append(c); 648 } else if (n > 0) { 649 myReply.setCharAt(n, c); 650 } 651 } 652 653 public boolean getValueBool(int idx) { 654 Matcher m = DCCppReply.match(myReply.toString(), myRegex, "gvb"); 655 if (m == null) { 656 log.error("DCCppReply '{}' not matched by '{}'", this.toString(), myRegex); 657 return (false); 658 } else if (idx <= m.groupCount()) { 659 return (!m.group(idx).equals("0")); 660 } else { 661 log.error("DCCppReply bool value index too big. idx = {} msg = {}", idx, this.toString()); 662 return (false); 663 } 664 } 665 666 public String getValueString(int idx) { 667 Matcher m = DCCppReply.match(myReply.toString(), myRegex, "gvs"); 668 if (m == null) { 669 log.error("DCCppReply '{}' not matched by '{}'", this.toString(), myRegex); 670 return (""); 671 } else if (idx <= m.groupCount()) { 672 return (m.group(idx)); 673 } else { 674 log.error("DCCppReply string value index too big. idx = {} msg = {}", idx, this.toString()); 675 return (""); 676 } 677 } 678 679 // is there a match at idx? 680 public boolean valueExists(int idx) { 681 Matcher m = DCCppReply.match(myReply.toString(), myRegex, "gvs"); 682 return (m != null) && (idx <= m.groupCount()); 683 } 684 685 public int getValueInt(int idx) { 686 Matcher m = DCCppReply.match(myReply.toString(), myRegex, "gvi"); 687 if (m == null) { 688 log.error("DCCppReply '{}' not matched by '{}'", this.toString(), myRegex); 689 return (0); 690 } else if (idx <= m.groupCount()) { 691 return (Integer.parseInt(m.group(idx))); 692 } else { 693 log.error("DCCppReply int value index too big. idx = {} msg = {}", idx, this.toString()); 694 return (0); 695 } 696 } 697 698 public double getValueDouble(int idx) { 699 Matcher m = DCCppReply.match(myReply.toString(), myRegex, "gvd"); 700 if (m == null) { 701 log.error("DCCppReply '{}' not matched by '{}'", this.toString(), myRegex); 702 return (0.0); 703 } else if (idx <= m.groupCount()) { 704 return (Double.parseDouble(m.group(idx))); 705 } else { 706 log.error("DCCppReply double value index too big. idx = {} msg = {}", idx, this.toString()); 707 return (0.0); 708 } 709 } 710 711 /* 712 * skipPrefix is not used at this point in time, but is defined as abstract 713 * in AbstractMRReply 714 */ 715 @Override 716 protected int skipPrefix(int index) { 717 return -1; 718 } 719 720 @Override 721 public int maxSize() { 722 return DCCppConstants.MAX_REPLY_SIZE; 723 } 724 725 public int getLength() { 726 return (myReply.length()); 727 } 728 729 /* 730 * Some notes on DCC++ messages and responses... 731 * 732 * Messages that have responses expected: 733 * t : <T REGISTER SPEED DIRECTION> 734 * f : (none) 735 * a : (none) 736 * T : <H ID THROW> 737 * w : (none) 738 * b : (none) 739 * W : <r CALLBACKNUM|CALLBACKSUB|CV CV_Value> 740 * B : <r CALLBACKNUM CALLBACKSUB|CV|Bit CV_Bit_Value> 741 * R : <r CALLBACKNUM|CALLBACKSUB|CV CV_Value> 742 * 1 : <p1> 743 * 0 : <p0> 744 * c : <a CURRENT> 745 * s : Series of status messages... 746 * <p[0,1]> Power state 747 * <T ...>Throttle responses from all registers 748 * <iDCC++ ... > Base station version and build date 749 * <H ID ADDR INDEX THROW> All turnout states. 750 * 751 * Unsolicited Replies: 752 * <Q snum [0,1]> Sensor reply. 753 * Debug messages: 754 * M : (none) 755 * P : (none) 756 * f : <f MEM> 757 * L : <M ... data ... > 758 */ 759 760 // ------------------------------------------------------------------- 761 // Message helper functions 762 // Core methods 763 764 protected boolean matches(String pat) { 765 return (match(this.toString(), pat, "Validator") != null); 766 } 767 768 protected static Matcher match(String s, String pat, String name) { 769 try { 770 Pattern p = Pattern.compile(pat); 771 Matcher m = p.matcher(s); 772 if (!m.matches()) { 773 log.trace("No Match {} Command: {} pattern {}", name, s, pat); 774 return (null); 775 } 776 return (m); 777 778 } catch (PatternSyntaxException e) { 779 log.error("Malformed DCC++ reply syntax! s = {}", pat); 780 return (null); 781 } catch (IllegalStateException e) { 782 log.error("Group called before match operation executed string = {}", s); 783 return (null); 784 } catch (IndexOutOfBoundsException e) { 785 log.error("Index out of bounds string = {}", s); 786 return (null); 787 } 788 } 789 790 public String getStationType() { 791 if (this.isStatusReply()) { 792 return (this.getValueString(1)); // 1st match in all versions 793 } else { 794 return ("Unknown"); 795 } 796 } 797 798 // build value is 3rd match in v3+, 2nd in previous 799 public String getBuildString() { 800 if (this.isStatusReply()) { 801 if (this.valueExists(3)) { 802 return(this.getValueString(3)); 803 } else { 804 return(this.getValueString(2)); 805 } 806 } else { 807 return("Unknown"); 808 } 809 } 810 811 // look for canonical version in 2nd match 812 public String getVersion() { 813 if (this.isStatusReply()) { 814 String s = this.getValueString(2); 815 if (jmri.Version.isCanonicalVersion(s)) { 816 return s; 817 } else { 818 return ("0.0.0"); 819 } 820 } else { 821 return ("Unknown"); 822 } 823 } 824 825 // Helper methods for Throttle and LocoState Replies 826 827 public int getLocoIdInt() { 828 if (this.isLocoStateReply() || this.isProgramLocoIdReply()) { 829 return (this.getValueInt(1)); 830 } else { 831 log.error("LocoId Parser called on non-LocoId message type {}", this.getOpCodeChar()); 832 return (-1); 833 } 834 } 835 836 public int getSpeedByteInt() { 837 if (this.isLocoStateReply()) { 838 return (this.getValueInt(3)); 839 } else { 840 log.error("ThrottleReply Parser called on non-Throttle message type {}", this.getOpCodeChar()); 841 return (0); 842 } 843 } 844 845 public int getFunctionsInt() { 846 if (this.isLocoStateReply()) { 847 return (this.getValueInt(4)); 848 } else { 849 log.error("ThrottleReply Parser called on non-Throttle message type {}", this.getOpCodeChar()); 850 return (0); 851 } 852 } 853 854 public String getFunctionsString() { 855 if (this.isLocoStateReply()) { 856 return new StringBuilder(StringUtils.leftPad(Integer.toBinaryString(this.getValueInt(4)), 29, "0")) 857 .reverse().toString(); 858 } else { 859 log.error("ThrottleReply Parser called on non-Throttle message type {}", this.getOpCodeChar()); 860 return ("not a locostate!"); 861 } 862 } 863 864 public String getRegisterString() { 865 return String.valueOf(getRegisterInt()); 866 } 867 868 public int getRegisterInt() { 869 if (this.isThrottleReply()) { 870 return (this.getValueInt(1)); 871 } else { 872 log.error("ThrottleReply Parser called on non-Throttle message type {}", this.getOpCodeChar()); 873 return (0); 874 } 875 } 876 877 public String getSpeedString() { 878 return String.valueOf(getSpeedInt()); 879 } 880 881 public int getSpeedInt() { 882 if (this.isThrottleReply()) { 883 return (this.getValueInt(2)); 884 } else if (this.isLocoStateReply()) { 885 int speed = this.getValueInt(3) & 0x7f; // drop direction bit 886 if (speed == 1) { 887 return -1; // special case for eStop 888 } 889 if (speed > 1) { 890 return speed - 1; // bump speeds down 1 due to eStop at 1 891 } 892 return 0; // stop is zero 893 } else { 894 log.error("ThrottleReply Parser called on non-Throttle message type {}", this.getOpCodeChar()); 895 return (0); 896 } 897 } 898 899 public boolean isEStop() { 900 return getSpeedInt() == -1; 901 } 902 903 public String getDirectionString() { 904 // Will return "Forward" (true) or "Reverse" (false) 905 return (getDirectionInt() == 1 ? "Forward" : "Reverse"); 906 } 907 908 public int getDirectionInt() { 909 // Will return 1 (true) or 0 (false) 910 if (this.isThrottleReply()) { 911 return (this.getValueInt(3)); 912 } else if (this.isLocoStateReply()) { 913 return this.getValueInt(3) >> 7; 914 } else { 915 log.error("ThrottleReply Parser called on non-ThrottleReply message type {}", this.getOpCodeChar()); 916 return (0); 917 } 918 } 919 920 public boolean getDirectionBool() { 921 // Will return true or false 922 if (this.isThrottleReply()) { 923 return (this.getValueBool(3)); 924 } else if (this.isLocoStateReply()) { 925 return ((this.getValueInt(3) >> 7) == 1); 926 } else { 927 log.error("ThrottleReply Parser called on non-ThrottleReply message type {}", this.getOpCodeChar()); 928 return (false); 929 } 930 } 931 932 public boolean getIsForward() { 933 return getDirectionBool(); 934 } 935 936 // ------------------------------------------------------ 937 // Helper methods for Turnout Replies 938 939 public String getTOIDString() { 940 if (this.isTurnoutReply() || this.isTurnoutIDReply()) { 941 return (this.getValueString(1)); 942 } else { 943 log.error("TurnoutReply Parser called on non-TurnoutReply message type {}", this.getOpCodeChar()); 944 return ("0"); 945 } 946 } 947 948 public int getTOIDInt() { 949 if (this.isTurnoutReply() || this.isTurnoutIDReply()) { 950 return (this.getValueInt(1)); 951 } else { 952 log.error("TurnoutReply Parser called on non-TurnoutReply message type {}", this.getOpCodeChar()); 953 return (0); 954 } 955 } 956 957 public String getTOAddressString() { 958 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { 959 return (this.getValueString(2)); 960 } else { 961 return ("-1"); 962 } 963 } 964 965 public int getTOAddressInt() { 966 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { 967 return (this.getValueInt(2)); 968 } else { 969 return (-1); 970 } 971 } 972 973 public String getTOAddressIndexString() { 974 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { 975 return (this.getValueString(3)); 976 } else { 977 return ("-1"); 978 } 979 } 980 981 public int getTOAddressIndexInt() { 982 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { 983 return (this.getValueInt(3)); 984 } else { 985 return (-1); 986 } 987 } 988 989 public int getTOPinInt() { 990 if (this.isTurnoutDefServoReply() || this.isTurnoutDefVpinReply()) { 991 return (this.getValueInt(2)); 992 } else { 993 return (-1); 994 } 995 } 996 997 public int getTOThrownPositionInt() { 998 if (this.isTurnoutDefServoReply()) { 999 return (this.getValueInt(3)); 1000 } else { 1001 return (-1); 1002 } 1003 } 1004 1005 public int getTOClosedPositionInt() { 1006 if (this.isTurnoutDefServoReply()) { 1007 return (this.getValueInt(4)); 1008 } else { 1009 return (-1); 1010 } 1011 } 1012 1013 public int getTOProfileInt() { 1014 if (this.isTurnoutDefServoReply()) { 1015 return (this.getValueInt(5)); 1016 } else { 1017 return (-1); 1018 } 1019 } 1020 1021 public String getTOStateString() { 1022 // Will return human readable state. To get string value for command, 1023 // use 1024 // getTOStateInt().toString() 1025 if (this.isTurnoutReply()) { 1026 return (this.getTOStateInt() == 1 ? "THROWN" : "CLOSED"); 1027 } else { 1028 log.error("TurnoutReply Parser called on non-TurnoutReply message type {}", this.getOpCodeChar()); 1029 return ("Not a Turnout"); 1030 } 1031 } 1032 1033 public int getTOStateInt() { 1034 // Will return 1 (true - thrown) or 0 (false - closed) 1035 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { // turnout 1036 // list 1037 // response 1038 return (this.getValueInt(4)); 1039 } else if (this.isTurnoutDefServoReply()) { // servo turnout 1040 return (this.getValueInt(6)); 1041 } else if (this.isTurnoutDefVpinReply()) { // vpin turnout 1042 return (this.getValueInt(3)); 1043 } else if (this.isTurnoutDefLCNReply()) { // LCN turnout 1044 return (this.getValueInt(2)); 1045 } else if (this.isTurnoutReply()) { // single turnout response 1046 return (this.getValueInt(2)); 1047 } else { 1048 log.error("TurnoutReply Parser called on non-TurnoutReply message type {}", this.getOpCodeChar()); 1049 return (0); 1050 } 1051 } 1052 1053 public boolean getTOIsThrown() { 1054 return (this.getTOStateInt() == 1); 1055 } 1056 1057 public boolean getTOIsClosed() { 1058 return (!this.getTOIsThrown()); 1059 } 1060 1061 // ------------------------------------------------------ 1062 // Helper methods for Program Replies 1063 1064 public String getCallbackNumString() { 1065 if (this.isProgramReply() || isProgramBitReply()) { 1066 return (this.getValueString(1)); 1067 } else { 1068 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1069 return ("0"); 1070 } 1071 } 1072 1073 public int getCallbackNumInt() { 1074 if (this.isProgramReply() || isProgramBitReply() ) { 1075 return(this.getValueInt(1)); 1076 } else { 1077 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1078 return(0); 1079 } 1080 } 1081 1082 public String getCallbackSubString() { 1083 if (this.isProgramReply() || isProgramBitReply()) { 1084 return (this.getValueString(2)); 1085 } else { 1086 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1087 return ("0"); 1088 } 1089 } 1090 1091 public int getCallbackSubInt() { 1092 if (this.isProgramReply() || isProgramBitReply()) { 1093 return (this.getValueInt(2)); 1094 } else { 1095 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1096 return (0); 1097 } 1098 } 1099 1100 public String getCVString() { 1101 if (this.isProgramReply()) { 1102 return (this.getValueString(3)); 1103 } else if (this.isProgramBitReply()) { 1104 return (this.getValueString(3)); 1105 } else if (this.isVerifyReply()) { 1106 return (this.getValueString(1)); 1107 } else if (this.isProgramReplyV4()) { 1108 return (this.getValueString(1)); 1109 } else if (this.isProgramBitReplyV4()) { 1110 return (this.getValueString(1)); 1111 } else { 1112 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1113 return ("0"); 1114 } 1115 } 1116 1117 public int getCVInt() { 1118 if (this.isProgramReply()) { 1119 return (this.getValueInt(3)); 1120 } else if (this.isProgramBitReply()) { 1121 return (this.getValueInt(3)); 1122 } else if (this.isVerifyReply()) { 1123 return (this.getValueInt(1)); 1124 } else if (this.isProgramReplyV4()) { 1125 return (this.getValueInt(1)); 1126 } else if (this.isProgramBitReplyV4()) { 1127 return (this.getValueInt(1)); 1128 } else { 1129 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1130 return (0); 1131 } 1132 } 1133 1134 public String getProgramBitString() { 1135 if (this.isProgramBitReply()) { 1136 return (this.getValueString(4)); 1137 } else if (this.isProgramBitReplyV4()) { 1138 return (this.getValueString(2)); 1139 } else { 1140 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1141 return ("0"); 1142 } 1143 } 1144 1145 public int getProgramBitInt() { 1146 if (this.isProgramBitReply()) { 1147 return (this.getValueInt(4)); 1148 } else { 1149 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1150 return (0); 1151 } 1152 } 1153 1154 public String getReadValueString() { 1155 if (this.isProgramReply()) { 1156 return (this.getValueString(4)); 1157 } else if (this.isProgramBitReply()) { 1158 return (this.getValueString(5)); 1159 } else if (this.isProgramBitReplyV4()) { 1160 return (this.getValueString(3)); 1161 } else if (this.isProgramReplyV4()) { 1162 return (this.getValueString(2)); 1163 } else if (this.isVerifyReply()) { 1164 return (this.getValueString(2)); 1165 } else { 1166 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1167 return ("0"); 1168 } 1169 } 1170 1171 public int getReadValueInt() { 1172 return (Integer.parseInt(this.getReadValueString())); 1173 } 1174 1175 public String getPowerDistrictName() { 1176 if (this.isNamedPowerReply()) { 1177 return (this.getValueString(2)); 1178 } else { 1179 log.error("NamedPowerReply Parser called on non-NamedPowerReply message type '{}' message '{}'", 1180 this.getOpCodeChar(), this.toString()); 1181 return (""); 1182 } 1183 } 1184 1185 public String getPowerDistrictStatus() { 1186 if (this.isNamedPowerReply()) { 1187 switch (this.getValueString(1)) { 1188 case DCCppConstants.POWER_OFF: 1189 return ("OFF"); 1190 case DCCppConstants.POWER_ON: 1191 return ("ON"); 1192 default: 1193 return ("OVERLOAD"); 1194 } 1195 } else { 1196 log.error("NamedPowerReply Parser called on non-NamedPowerReply message type {} message {}", 1197 this.getOpCodeChar(), this.toString()); 1198 return (""); 1199 } 1200 } 1201 1202 public String getCurrentString() { 1203 if (this.isCurrentReply()) { 1204 if (this.isNamedCurrentReply()) { 1205 return (this.getValueString(2)); 1206 } 1207 return (this.getValueString(1)); 1208 } else { 1209 log.error("CurrentReply Parser called on non-CurrentReply message type {} message {}", this.getOpCodeChar(), 1210 this.toString()); 1211 return ("0"); 1212 } 1213 } 1214 1215 public int getCurrentInt() { 1216 return (Integer.parseInt(this.getCurrentString())); 1217 } 1218 1219 public String getMeterName() { 1220 if (this.isMeterReply()) { 1221 return (this.getValueString(1)); 1222 } else { 1223 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1224 this.toString()); 1225 return (""); 1226 } 1227 } 1228 1229 public double getMeterValue() { 1230 if (this.isMeterReply()) { 1231 return (this.getValueDouble(2)); 1232 } else { 1233 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1234 this.toString()); 1235 return (0.0); 1236 } 1237 } 1238 1239 public String getMeterType() { 1240 if (this.isMeterReply()) { 1241 String t = getValueString(3); 1242 if (t.equals(DCCppConstants.VOLTAGE) || t.equals(DCCppConstants.CURRENT)) { 1243 return (t); 1244 } else { 1245 log.warn("Meter Type '{}' is not valid type in message '{}'", t, this.toString()); 1246 return (""); 1247 } 1248 } else { 1249 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1250 this.toString()); 1251 return (""); 1252 } 1253 } 1254 1255 public jmri.Meter.Unit getMeterUnit() { 1256 if (this.isMeterReply()) { 1257 String us = this.getValueString(4); 1258 AbstractXmlAdapter.EnumIO<jmri.Meter.Unit> map = 1259 new AbstractXmlAdapter.EnumIoNames<>(jmri.Meter.Unit.class); 1260 return (map.inputFromString(us)); 1261 } else { 1262 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1263 this.toString()); 1264 return (jmri.Meter.Unit.NoPrefix); 1265 } 1266 } 1267 1268 public double getMeterMinValue() { 1269 if (this.isMeterReply()) { 1270 return (this.getValueDouble(5)); 1271 } else { 1272 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1273 this.toString()); 1274 return (0.0); 1275 } 1276 } 1277 1278 public double getMeterMaxValue() { 1279 if (this.isMeterReply()) { 1280 return (this.getValueDouble(6)); 1281 } else { 1282 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1283 this.toString()); 1284 return (0.0); 1285 } 1286 } 1287 1288 public double getMeterResolution() { 1289 if (this.isMeterReply()) { 1290 return (this.getValueDouble(7)); 1291 } else { 1292 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1293 this.toString()); 1294 return (0.0); 1295 } 1296 } 1297 1298 public double getMeterWarnValue() { 1299 if (this.isMeterReply()) { 1300 return (this.getValueDouble(8)); 1301 } else { 1302 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1303 this.toString()); 1304 return (0.0); 1305 } 1306 } 1307 1308 public boolean isMeterTypeVolt() { 1309 if (this.isMeterReply()) { 1310 return (this.getMeterType().equals(DCCppConstants.VOLTAGE)); 1311 } else { 1312 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1313 this.toString()); 1314 return (false); 1315 } 1316 } 1317 1318 public boolean isMeterTypeCurrent() { 1319 if (this.isMeterReply()) { 1320 return (this.getMeterType().equals(DCCppConstants.CURRENT)); 1321 } else { 1322 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1323 this.toString()); 1324 return (false); 1325 } 1326 } 1327 1328 public boolean getPowerBool() { 1329 if (this.isPowerReply()) { 1330 return (this.getValueString(1).equals(DCCppConstants.POWER_ON)); 1331 } else { 1332 log.error("PowerReply Parser called on non-PowerReply message type {} message {}", this.getOpCodeChar(), 1333 this.toString()); 1334 return (false); 1335 } 1336 } 1337 1338 public String getTurnoutDefNumString() { 1339 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { 1340 return (this.getValueString(1)); 1341 } else { 1342 log.error("TurnoutDefReply Parser called on non-TurnoutDefReply message type {}", this.getOpCodeChar()); 1343 return ("0"); 1344 } 1345 } 1346 1347 public int getTurnoutDefNumInt() { 1348 return (Integer.parseInt(this.getTurnoutDefNumString())); 1349 } 1350 1351 public String getTurnoutDefAddrString() { 1352 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { 1353 return (this.getValueString(2)); 1354 } else { 1355 log.error("TurnoutDefReply Parser called on non-TurnoutDefReply message type {}", this.getOpCodeChar()); 1356 return ("0"); 1357 } 1358 } 1359 1360 public int getTurnoutDefAddrInt() { 1361 return (Integer.parseInt(this.getTurnoutDefAddrString())); 1362 } 1363 1364 public String getTurnoutDefSubAddrString() { 1365 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { 1366 return (this.getValueString(3)); 1367 } else { 1368 log.error("TurnoutDefReply Parser called on non-TurnoutDefReply message type {}", this.getOpCodeChar()); 1369 return ("0"); 1370 } 1371 } 1372 1373 public int getTurnoutDefSubAddrInt() { 1374 return (Integer.parseInt(this.getTurnoutDefSubAddrString())); 1375 } 1376 1377 public String getOutputNumString() { 1378 if (this.isOutputDefReply() || this.isOutputCmdReply()) { 1379 return (this.getValueString(1)); 1380 } else { 1381 log.error("OutputAddReply Parser called on non-OutputAddReply message type {}", this.getOpCodeChar()); 1382 return ("0"); 1383 } 1384 } 1385 1386 public int getOutputNumInt() { 1387 return (Integer.parseInt(this.getOutputNumString())); 1388 } 1389 1390 public String getOutputListPinString() { 1391 if (this.isOutputDefReply()) { 1392 return (this.getValueString(2)); 1393 } else { 1394 log.error("OutputAddReply Parser called on non-OutputAddReply message type {}", this.getOpCodeChar()); 1395 return ("0"); 1396 } 1397 } 1398 1399 public int getOutputListPinInt() { 1400 return (Integer.parseInt(this.getOutputListPinString())); 1401 } 1402 1403 public String getOutputListIFlagString() { 1404 if (this.isOutputDefReply()) { 1405 return (this.getValueString(3)); 1406 } else { 1407 log.error("OutputListReply Parser called on non-OutputListReply message type {}", this.getOpCodeChar()); 1408 return ("0"); 1409 } 1410 } 1411 1412 public int getOutputListIFlagInt() { 1413 return (Integer.parseInt(this.getOutputListIFlagString())); 1414 } 1415 1416 public String getOutputListStateString() { 1417 if (this.isOutputDefReply()) { 1418 return (this.getValueString(4)); 1419 } else { 1420 log.error("OutputListReply Parser called on non-OutputListReply message type {}", this.getOpCodeChar()); 1421 return ("0"); 1422 } 1423 } 1424 1425 public int getOutputListStateInt() { 1426 return (Integer.parseInt(this.getOutputListStateString())); 1427 } 1428 1429 public boolean getOutputCmdStateBool() { 1430 if (this.isOutputCmdReply()) { 1431 return ((this.getValueBool(2))); 1432 } else { 1433 log.error("OutputCmdReply Parser called on non-OutputCmdReply message type {}", this.getOpCodeChar()); 1434 return (false); 1435 } 1436 } 1437 1438 public String getOutputCmdStateString() { 1439 if (this.isOutputCmdReply()) { 1440 return (this.getValueBool(2) ? "HIGH" : "LOW"); 1441 } else { 1442 log.error("OutputCmdReply Parser called on non-OutputCmdReply message type {}", this.getOpCodeChar()); 1443 return ("0"); 1444 } 1445 } 1446 1447 public int getOutputCmdStateInt() { 1448 return (this.getOutputCmdStateBool() ? 1 : 0); 1449 } 1450 1451 public boolean getOutputIsHigh() { 1452 return (this.getOutputCmdStateBool()); 1453 } 1454 1455 public boolean getOutputIsLow() { 1456 return (!this.getOutputCmdStateBool()); 1457 } 1458 1459 public String getSensorDefNumString() { 1460 if (this.isSensorDefReply()) { 1461 return (this.getValueString(1)); 1462 } else { 1463 log.error("SensorDefReply Parser called on non-SensorDefReply message type {}", this.getOpCodeChar()); 1464 return ("0"); 1465 } 1466 } 1467 1468 public int getSensorDefNumInt() { 1469 return (Integer.parseInt(this.getSensorDefNumString())); 1470 } 1471 1472 public String getSensorDefPinString() { 1473 if (this.isSensorDefReply()) { 1474 return (this.getValueString(2)); 1475 } else { 1476 log.error("SensorDefReply Parser called on non-SensorDefReply message type {}", this.getOpCodeChar()); 1477 return ("0"); 1478 } 1479 } 1480 1481 public int getSensorDefPinInt() { 1482 return (Integer.parseInt(this.getSensorDefPinString())); 1483 } 1484 1485 public String getSensorDefPullupString() { 1486 if (this.isSensorDefReply()) { 1487 return (this.getSensorDefPullupBool() ? "Pullup" : "NoPullup"); 1488 } else { 1489 log.error("SensorDefReply Parser called on non-SensorDefReply message type {}", this.getOpCodeChar()); 1490 return ("Not a Sensor"); 1491 } 1492 } 1493 1494 public int getSensorDefPullupInt() { 1495 if (this.isSensorDefReply()) { 1496 return (this.getValueInt(3)); 1497 } else { 1498 log.error("SensorDefReply Parser called on non-SensorDefReply message type {}", this.getOpCodeChar()); 1499 return (0); 1500 } 1501 } 1502 1503 public boolean getSensorDefPullupBool() { 1504 if (this.isSensorDefReply()) { 1505 return (this.getValueBool(3)); 1506 } else { 1507 log.error("SensorDefReply Parser called on non-SensorDefReply message type {}", this.getOpCodeChar()); 1508 return (false); 1509 } 1510 } 1511 1512 public String getSensorNumString() { 1513 if (this.isSensorReply()) { 1514 return (this.getValueString(1)); 1515 } else { 1516 log.error("SensorReply Parser called on non-SensorReply message type {}", this.getOpCodeChar()); 1517 return ("0"); 1518 } 1519 } 1520 1521 public int getSensorNumInt() { 1522 return (Integer.parseInt(this.getSensorNumString())); 1523 } 1524 1525 public String getSensorStateString() { 1526 if (this.isSensorReply()) { 1527 return (this.myRegex.equals(DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX) ? "Active" : "Inactive"); 1528 } else { 1529 log.error("SensorReply Parser called on non-SensorReply message type {}", this.getOpCodeChar()); 1530 return ("Not a Sensor"); 1531 } 1532 } 1533 1534 public int getSensorStateInt() { 1535 if (this.isSensorReply()) { 1536 return (this.myRegex.equals(DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX) ? 1 : 0); 1537 } else { 1538 log.error("SensorReply Parser called on non-SensorReply message type {}", this.getOpCodeChar()); 1539 return (0); 1540 } 1541 } 1542 1543 public boolean getSensorIsActive() { 1544 return (this.myRegex.equals(DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX)); 1545 } 1546 1547 public boolean getSensorIsInactive() { 1548 return (this.myRegex.equals(DCCppConstants.SENSOR_INACTIVE_REPLY_REGEX)); 1549 } 1550 1551 public int getCommTypeInt() { 1552 if (this.isCommTypeReply()) { 1553 return (this.getValueInt(1)); 1554 } else { 1555 log.error("CommTypeReply Parser called on non-CommTypeReply message type {}", this.getOpCodeChar()); 1556 return (0); 1557 } 1558 } 1559 1560 public String getCommTypeValueString() { 1561 if (this.isCommTypeReply()) { 1562 return (this.getValueString(2)); 1563 } else { 1564 log.error("CommTypeReply Parser called on non-CommTypeReply message type {}", this.getOpCodeChar()); 1565 return ("N/A"); 1566 } 1567 } 1568 1569 public ArrayList<Integer> getTurnoutIDList() { 1570 ArrayList<Integer> ids=new ArrayList<Integer>(); 1571 if (this.isTurnoutIDsReply()) { 1572 String idList = this.getValueString(1); 1573 if (!idList.isEmpty()) { 1574 String[] idStrings = idList.split(" "); 1575 for (String idString : idStrings) { 1576 ids.add(Integer.parseInt(idString)); 1577 } 1578 } 1579 } else { 1580 log.error("TurnoutIDsReply Parser called on non-TurnoutIDsReply message type {}", this.getOpCodeChar()); 1581 } 1582 return ids; 1583 } 1584 public String getTurnoutStateString() { 1585 if (this.isTurnoutIDReply()) { 1586 return (this.getValueString(2)); 1587 } else { 1588 log.error("getTurnoutIDString Parser called on non-getTurnoutIDString message type {}", this.getOpCodeChar()); 1589 return ("0"); 1590 } 1591 } 1592 public String getTurnoutDescString() { 1593 if (this.isTurnoutIDReply()) { 1594 return (this.getValueString(3)); 1595 } else { 1596 log.error("getTurnoutIDString Parser called on non-getTurnoutIDString message type {}", this.getOpCodeChar()); 1597 return ("0"); 1598 } 1599 } 1600 public String getClockMinutesString() { 1601 if (this.isClockReply()) { 1602 return (this.getValueString(1)); 1603 } else { 1604 log.error("getClockTimeString Parser called on non-getClockTimeString message type {}", this.getOpCodeChar()); 1605 return ("0"); 1606 } 1607 } 1608 public int getClockMinutesInt() { 1609 return (Integer.parseInt(this.getClockMinutesString())); 1610 } 1611 public String getClockRateString() { 1612 if (this.isClockReply()) { 1613 return (this.getValueString(2)); 1614 } else { 1615 log.error("getClockRateString Parser called on non-getClockRateString message type {}", this.getOpCodeChar()); 1616 return ("0"); 1617 } 1618 } 1619 public int getClockRateInt() { 1620 return (Integer.parseInt(this.getClockRateString())); 1621 } 1622 1623 // <@ 0 8 "message text"> 1624 public boolean isLCDTextReply() { 1625 return (this.matches(DCCppConstants.LCD_TEXT_REPLY_REGEX)); 1626 } 1627 public String getLCDTextString() { 1628 if (this.isLCDTextReply()) { 1629 return (this.getValueString(3)); 1630 } else { 1631 log.error("getLCDTextString Parser called on non-LCDTextString message type {}", this.getOpCodeChar()); 1632 return ("error"); 1633 } 1634 } 1635 public String getLCDDisplayNumString() { 1636 if (this.isLCDTextReply()) { 1637 return (this.getValueString(1)); 1638 } else { 1639 log.error("getLCDDisplayNumString Parser called on non-LCDTextString message type {}", this.getOpCodeChar()); 1640 return ("error"); 1641 } 1642 } 1643 public int getLCDDisplayNumInt() { 1644 return (Integer.parseInt(this.getLCDDisplayNumString())); 1645 } 1646 public String getLCDLineNumString() { 1647 if (this.isLCDTextReply()) { 1648 return (this.getValueString(2)); 1649 } else { 1650 log.error("getLCDLineNumString Parser called on non-LCDTextString message type {}", this.getOpCodeChar()); 1651 return ("error"); 1652 } 1653 } 1654 public int getLCDLineNumInt() { 1655 return (Integer.parseInt(this.getLCDLineNumString())); 1656 } 1657 1658 // ------------------------------------------------------------------- 1659 1660 // Message Identification functions 1661 public boolean isThrottleReply() { 1662 return (this.getOpCodeChar() == DCCppConstants.THROTTLE_REPLY); 1663 } 1664 1665 public boolean isTurnoutReply() { 1666 return (this.getOpCodeChar() == DCCppConstants.TURNOUT_REPLY); 1667 } 1668 1669 public boolean isTurnoutCmdReply() { 1670 return (this.matches(DCCppConstants.TURNOUT_REPLY_REGEX)); 1671 } 1672 1673 public boolean isProgramReply() { 1674 return (this.matches(DCCppConstants.PROGRAM_REPLY_REGEX)); 1675 } 1676 1677 public boolean isProgramReplyV4() { 1678 return (this.matches(DCCppConstants.PROGRAM_REPLY_V4_REGEX)); 1679 } 1680 1681 public boolean isProgramLocoIdReply() { 1682 return (this.matches(DCCppConstants.PROGRAM_LOCOID_REPLY_REGEX)); 1683 } 1684 1685 public boolean isVerifyReply() { 1686 return (this.matches(DCCppConstants.PROGRAM_VERIFY_REPLY_REGEX)); 1687 } 1688 1689 public boolean isProgramBitReply() { 1690 return (this.matches(DCCppConstants.PROGRAM_BIT_REPLY_REGEX)); 1691 } 1692 1693 public boolean isProgramBitReplyV4() { 1694 return (this.matches(DCCppConstants.PROGRAM_BIT_REPLY_V4_REGEX)); 1695 } 1696 1697 public boolean isPowerReply() { 1698 return (this.getOpCodeChar() == DCCppConstants.POWER_REPLY); 1699 } 1700 1701 public boolean isNamedPowerReply() { 1702 return (this.matches(DCCppConstants.TRACK_POWER_REPLY_NAMED_REGEX)); 1703 } 1704 1705 public boolean isMaxNumSlotsReply() { 1706 return (this.matches(DCCppConstants.MAXNUMSLOTS_REPLY_REGEX)); 1707 } 1708 1709 public boolean isDiagReply() { 1710 return (this.matches(DCCppConstants.DIAG_REPLY_REGEX)); 1711 } 1712 1713 public boolean isCurrentReply() { 1714 return (this.getOpCodeChar() == DCCppConstants.CURRENT_REPLY); 1715 } 1716 1717 public boolean isNamedCurrentReply() { 1718 return (this.matches(DCCppConstants.CURRENT_REPLY_NAMED_REGEX)); 1719 } 1720 1721 public boolean isMeterReply() { 1722 return (this.matches(DCCppConstants.METER_REPLY_REGEX)); 1723 } 1724 1725 public boolean isSensorReply() { 1726 return ((this.getOpCodeChar() == DCCppConstants.SENSOR_REPLY) || 1727 (this.getOpCodeChar() == DCCppConstants.SENSOR_REPLY_H) || 1728 (this.getOpCodeChar() == DCCppConstants.SENSOR_REPLY_L)); 1729 } 1730 1731 public boolean isSensorDefReply() { 1732 return (this.matches(DCCppConstants.SENSOR_DEF_REPLY_REGEX)); 1733 } 1734 1735 public boolean isTurnoutDefReply() { 1736 return (this.matches(DCCppConstants.TURNOUT_DEF_REPLY_REGEX)); 1737 } 1738 1739 public boolean isTurnoutDefDCCReply() { 1740 return (this.matches(DCCppConstants.TURNOUT_DEF_DCC_REPLY_REGEX)); 1741 } 1742 1743 public boolean isTurnoutDefServoReply() { 1744 return (this.matches(DCCppConstants.TURNOUT_DEF_SERVO_REPLY_REGEX)); 1745 } 1746 1747 public boolean isTurnoutDefVpinReply() { 1748 return (this.matches(DCCppConstants.TURNOUT_DEF_VPIN_REPLY_REGEX)); 1749 } 1750 1751 public boolean isTurnoutDefLCNReply() { 1752 return (this.matches(DCCppConstants.TURNOUT_DEF_LCN_REPLY_REGEX)); 1753 } 1754 1755 public boolean isMADCFailReply() { 1756 return (this.getOpCodeChar() == DCCppConstants.MADC_FAIL_REPLY); 1757 } 1758 1759 public boolean isMADCSuccessReply() { 1760 return (this.getOpCodeChar() == DCCppConstants.MADC_SUCCESS_REPLY); 1761 } 1762 1763 public boolean isStatusReply() { 1764 return (this.getOpCodeChar() == DCCppConstants.STATUS_REPLY); 1765 } 1766 1767 public boolean isOutputReply() { 1768 return (this.getOpCodeChar() == DCCppConstants.OUTPUT_REPLY); 1769 } 1770 1771 public boolean isOutputDefReply() { 1772 return (this.matches(DCCppConstants.OUTPUT_DEF_REPLY_REGEX)); 1773 } 1774 1775 public boolean isOutputCmdReply() { 1776 return (this.matches(DCCppConstants.OUTPUT_REPLY_REGEX)); 1777 } 1778 1779 public boolean isCommTypeReply() { 1780 return (this.matches(DCCppConstants.COMM_TYPE_REPLY_REGEX)); 1781 } 1782 1783 public boolean isWriteEepromReply() { 1784 return (this.matches(DCCppConstants.WRITE_EEPROM_REPLY_REGEX)); 1785 } 1786 1787 public boolean isLocoStateReply() { 1788 return (this.getOpCodeChar() == DCCppConstants.LOCO_STATE_REPLY); 1789 } 1790 1791 public boolean isTurnoutIDsReply() { 1792 return (this.matches(DCCppConstants.TURNOUT_IDS_REPLY_REGEX)); 1793 } 1794 public boolean isTurnoutIDReply() { 1795 return (this.matches(DCCppConstants.TURNOUT_ID_REPLY_REGEX)); 1796 } 1797 public boolean isClockReply() { 1798 return (this.matches(DCCppConstants.CLOCK_REPLY_REGEX)); 1799 } 1800 1801 public boolean isTrackManagerReply() { 1802 return (this.matches(DCCppConstants.TRACKMANAGER_REPLY_REGEX)); 1803 } 1804 1805 public boolean isValidReplyFormat() { 1806 if ((this.matches(DCCppConstants.THROTTLE_REPLY_REGEX)) || 1807 (this.matches(DCCppConstants.MAXNUMSLOTS_REPLY_REGEX)) || 1808 (this.matches(DCCppConstants.TURNOUT_REPLY_REGEX)) || 1809 (this.matches(DCCppConstants.PROGRAM_REPLY_REGEX)) || 1810 (this.matches(DCCppConstants.PROGRAM_REPLY_V4_REGEX)) || 1811 (this.matches(DCCppConstants.PROGRAM_LOCOID_REPLY_REGEX)) || 1812 (this.matches(DCCppConstants.PROGRAM_VERIFY_REPLY_REGEX)) || 1813 (this.matches(DCCppConstants.TRACK_POWER_REPLY_REGEX)) || 1814 (this.matches(DCCppConstants.TRACK_POWER_REPLY_NAMED_REGEX)) || 1815 (this.matches(DCCppConstants.CURRENT_REPLY_REGEX)) || 1816 (this.matches(DCCppConstants.CURRENT_REPLY_NAMED_REGEX)) || 1817 (this.matches(DCCppConstants.METER_REPLY_REGEX)) || 1818 (this.matches(DCCppConstants.SENSOR_REPLY_REGEX)) || 1819 (this.matches(DCCppConstants.SENSOR_DEF_REPLY_REGEX)) || 1820 (this.matches(DCCppConstants.SENSOR_INACTIVE_REPLY_REGEX)) || 1821 (this.matches(DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX)) || 1822 (this.matches(DCCppConstants.OUTPUT_REPLY_REGEX)) || 1823 (this.matches(DCCppConstants.OUTPUT_DEF_REPLY_REGEX)) || 1824 (this.matches(DCCppConstants.MADC_FAIL_REPLY_REGEX)) || 1825 (this.matches(DCCppConstants.MADC_SUCCESS_REPLY_REGEX)) || 1826 (this.matches(DCCppConstants.STATUS_REPLY_REGEX)) || 1827 (this.matches(DCCppConstants.STATUS_REPLY_BSC_REGEX)) || 1828 (this.matches(DCCppConstants.STATUS_REPLY_ESP32_REGEX)) || 1829 (this.matches(DCCppConstants.STATUS_REPLY_DCCEX_REGEX)) || 1830 (this.matches(DCCppConstants.LOCO_STATE_REGEX)) || 1831 (this.matches(DCCppConstants.TURNOUT_IDS_REPLY_REGEX)) || 1832 (this.matches(DCCppConstants.TURNOUT_ID_REPLY_REGEX)) || 1833 (this.matches(DCCppConstants.TURNOUT_IMPL_REGEX)) || 1834 (this.matches(DCCppConstants.TURNOUT_DEF_REPLY_REGEX)) || 1835 (this.matches(DCCppConstants.TURNOUT_DEF_DCC_REPLY_REGEX)) || 1836 (this.matches(DCCppConstants.TURNOUT_DEF_SERVO_REPLY_REGEX)) || 1837 (this.matches(DCCppConstants.TURNOUT_DEF_VPIN_REPLY_REGEX)) || 1838 (this.matches(DCCppConstants.TURNOUT_DEF_LCN_REPLY_REGEX)) || 1839 (this.matches(DCCppConstants.LCD_TEXT_REPLY_REGEX)) || 1840 (this.matches(DCCppConstants.CLOCK_REPLY_REGEX)) || 1841 (this.matches(DCCppConstants.DIAG_REPLY_REGEX)) || 1842 (this.matches(DCCppConstants.TRACKMANAGER_REPLY_REGEX))) { 1843 return (true); 1844 } else { 1845 return (false); 1846 } 1847 } 1848 1849 // initialize logging 1850 private final static Logger log = LoggerFactory.getLogger(DCCppReply.class); 1851 1852}