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 (isRosterIDsReply()) { 295 text = "RosterIDs:" + getRosterIDList(); 296 break; 297 } else if (isRosterIDReply()) { 298 text = "RosterID:" + getRosterIDString(); 299 text += " Desc:'" + getRosterDescString() + "'"; 300 text += " Fkeys:'" + getRosterFKeysString() + "'"; 301 break; 302 } else if (isAutomationIDsReply()) { 303 text = "AutomationIDs:" + getAutomationIDList(); 304 break; 305 } else if (isAutomationIDReply()) { 306 text = "AutomationID:" + getAutomationIDString(); 307 text += " Type:" + getAutomationTypeString(); 308 text += " Desc:'" + getAutomationDescString() + "'"; 309 break; 310 } else if (isCurrentMaxesReply()) { 311 text = "CurrentMaxes:" + getCurrentMaxesList(); 312 break; 313 } else if (isCurrentValuesReply()) { 314 text = "CurrentValues:" + getCurrentValuesList(); 315 break; 316 } else if (isClockReply()) { 317 String hhmm = String.format("%02d:%02d", 318 getClockMinutesInt() / 60, 319 getClockMinutesInt() % 60); 320 text = "FastClock Reply: " + hhmm; 321 if (!getClockRateString().isEmpty()) { 322 text += ", Rate:" + getClockRateString(); 323 } 324 break; 325 } 326 text = "Unknown Message: '" + toString() + "'"; 327 break; 328 case DCCppConstants.TRACKMANAGER_CMD: 329 text = "TrackManager Letter:" + getTrackManagerLetter() + " Mode:" + getTrackManagerMode(); 330 break; 331 case DCCppConstants.LCD_TEXT_CMD: 332 text = "LCD Text '" + getLCDTextString() + "', disp " + getLCDDisplayNumString() + ", line " + getLCDLineNumString(); 333 break; 334 335 default: 336 text = "Unrecognized reply: '" + toString() + "'"; 337 } 338 339 return text; 340 } 341 342 /** 343 * Generate properties list for certain replies 344 * 345 * @return list of all properties as a string 346 **/ 347 public String getPropertiesAsString() { 348 StringBuilder text = new StringBuilder(); 349 StringBuilder comma = new StringBuilder(); 350 switch (getOpCodeChar()) { 351 case DCCppConstants.TURNOUT_REPLY: 352 case DCCppConstants.SENSOR_REPLY: 353 case DCCppConstants.OUTPUT_REPLY: 354 // write out properties in comment 355 getProperties().forEach((key, value) -> { 356 text.append(comma).append(key).append(":").append(value); 357 comma.setLength(0); 358 comma.append(","); 359 }); 360 361 break; 362 default: 363 break; 364 } 365 return text.toString(); 366 } 367 368 /** 369 * build a propertylist from reply values. 370 * 371 * @return properties hashmap 372 **/ 373 public LinkedHashMap<String, Object> getProperties() { 374 LinkedHashMap<String, Object> properties = new LinkedHashMap<>(); 375 switch (getOpCodeChar()) { 376 case DCCppConstants.TURNOUT_REPLY: 377 if (isTurnoutDefDCCReply()) { 378 properties.put(DCCppConstants.PROP_TYPE, DCCppConstants.TURNOUT_TYPE_DCC); 379 properties.put(DCCppConstants.PROP_ID, getTOIDInt()); 380 properties.put(DCCppConstants.PROP_ADDRESS, getTOAddressInt()); 381 properties.put(DCCppConstants.PROP_INDEX, getTOAddressIndexInt()); 382 // if we are able to parse the address and index we can convert it 383 // to a standard DCC address for display. 384 if (getTOAddressInt() != -1 && getTOAddressIndexInt() != -1) { 385 int boardAddr = getTOAddressInt(); 386 int boardIndex = getTOAddressIndexInt(); 387 int dccAddress = (((boardAddr - 1) * 4) + boardIndex) + 1; 388 properties.put(DCCppConstants.PROP_DCCADDRESS, dccAddress); 389 } 390 } else if (isTurnoutDefServoReply()) { 391 properties.put(DCCppConstants.PROP_TYPE, DCCppConstants.TURNOUT_TYPE_SERVO); 392 properties.put(DCCppConstants.PROP_ID, getTOIDInt()); 393 properties.put(DCCppConstants.PROP_PIN, getTOPinInt()); 394 properties.put(DCCppConstants.PROP_THROWNPOS, getTOThrownPositionInt()); 395 properties.put(DCCppConstants.PROP_CLOSEDPOS, getTOClosedPositionInt()); 396 properties.put(DCCppConstants.PROP_PROFILE, getTOProfileInt()); 397 } else if (isTurnoutDefVpinReply()) { 398 properties.put(DCCppConstants.PROP_TYPE, DCCppConstants.TURNOUT_TYPE_VPIN); 399 properties.put(DCCppConstants.PROP_ID, getTOIDInt()); 400 properties.put(DCCppConstants.PROP_PIN, getTOPinInt()); 401 } else if (isTurnoutDefLCNReply()) { 402 properties.put(DCCppConstants.PROP_TYPE, DCCppConstants.TURNOUT_TYPE_LCN); 403 properties.put(DCCppConstants.PROP_ID, getTOIDInt()); 404 } 405 break; 406 case DCCppConstants.SENSOR_REPLY: 407 if (isSensorDefReply()) { 408 properties.put(DCCppConstants.PROP_TYPE, DCCppConstants.SENSOR_TYPE); 409 properties.put(DCCppConstants.PROP_ID, getSensorDefNumInt()); 410 properties.put(DCCppConstants.PROP_PIN, getSensorDefPinInt()); 411 properties.put(DCCppConstants.PROP_PULLUP, getSensorDefPullupBool()); 412 } 413 break; 414 case DCCppConstants.OUTPUT_REPLY: 415 if (isOutputDefReply()) { 416 properties.put(DCCppConstants.PROP_TYPE, DCCppConstants.OUTPUT_TYPE); 417 properties.put(DCCppConstants.PROP_ID, getOutputNumInt()); 418 properties.put(DCCppConstants.PROP_PIN, getOutputListPinInt()); 419 properties.put(DCCppConstants.PROP_IFLAG, getOutputListIFlagInt()); 420 } 421 break; 422 default: 423 break; 424 } 425 return properties; 426 } 427 428 public void parseReply(String s) { 429 DCCppReply r = DCCppReply.parseDCCppReply(s); 430 log.debug("in parseReply() string: {}", s); 431 if (!(r.toString().isBlank())) { 432 this.myRegex = r.myRegex; 433 this.myReply = r.myReply; 434 this._nDataChars = r._nDataChars; 435 log.trace("copied: this: {}", this); 436 } 437 } 438 439 /// 440 /// 441 /// TODO: Stopped Refactoring to StringBuilder here 12/12/15 442 /// 443 /// 444 445 /** 446 * Parses a string and generates a DCCppReply from the string contents 447 * 448 * @param s String to be parsed 449 * @return DCCppReply or empty string if not a valid formatted string 450 */ 451 @Nonnull 452 public static DCCppReply parseDCCppReply(String s) { 453 454 if (log.isTraceEnabled()) { 455 log.trace("Parse charAt(0): {}", s.charAt(0)); 456 } 457 DCCppReply r = new DCCppReply(s); 458 switch (s.charAt(0)) { 459 case DCCppConstants.STATUS_REPLY: 460 if (s.matches(DCCppConstants.STATUS_REPLY_BSC_REGEX)) { 461 log.debug("BSC Status Reply: '{}'", r); 462 r.myRegex = DCCppConstants.STATUS_REPLY_BSC_REGEX; 463 } else if (s.matches(DCCppConstants.STATUS_REPLY_ESP32_REGEX)) { 464 log.debug("ESP32 Status Reply: '{}'", r); 465 r.myRegex = DCCppConstants.STATUS_REPLY_ESP32_REGEX; 466 } else if (s.matches(DCCppConstants.STATUS_REPLY_REGEX)) { 467 log.debug("Original Status Reply: '{}'", r); 468 r.myRegex = DCCppConstants.STATUS_REPLY_REGEX; 469 } else if (s.matches(DCCppConstants.STATUS_REPLY_DCCEX_REGEX)) { 470 log.debug("DCC-EX Status Reply: '{}'", r); 471 r.myRegex = DCCppConstants.STATUS_REPLY_DCCEX_REGEX; 472 } 473 return (r); 474 case DCCppConstants.THROTTLE_REPLY: 475 if (s.matches(DCCppConstants.THROTTLE_REPLY_REGEX)) { 476 log.debug("Throttle Reply: '{}'", r); 477 r.myRegex = DCCppConstants.THROTTLE_REPLY_REGEX; 478 } 479 return (r); 480 case DCCppConstants.TURNOUT_REPLY: 481 // the order of checking the reply here is critical as both the 482 // TURNOUT_DEF_REPLY and TURNOUT_REPLY regex strings start with 483 // the same strings but have different meanings. 484 if (s.matches(DCCppConstants.TURNOUT_DEF_REPLY_REGEX)) { 485 r.myRegex = DCCppConstants.TURNOUT_DEF_REPLY_REGEX; 486 } else if (s.matches(DCCppConstants.TURNOUT_DEF_DCC_REPLY_REGEX)) { 487 r.myRegex = DCCppConstants.TURNOUT_DEF_DCC_REPLY_REGEX; 488 } else if (s.matches(DCCppConstants.TURNOUT_DEF_SERVO_REPLY_REGEX)) { 489 r.myRegex = DCCppConstants.TURNOUT_DEF_SERVO_REPLY_REGEX; 490 } else if (s.matches(DCCppConstants.TURNOUT_DEF_VPIN_REPLY_REGEX)) { 491 r.myRegex = DCCppConstants.TURNOUT_DEF_VPIN_REPLY_REGEX; 492 } else if (s.matches(DCCppConstants.TURNOUT_DEF_LCN_REPLY_REGEX)) { 493 r.myRegex = DCCppConstants.TURNOUT_DEF_LCN_REPLY_REGEX; 494 } else if (s.matches(DCCppConstants.TURNOUT_REPLY_REGEX)) { 495 r.myRegex = DCCppConstants.TURNOUT_REPLY_REGEX; 496 } else if (s.matches(DCCppConstants.MADC_FAIL_REPLY_REGEX)) { 497 r.myRegex = DCCppConstants.MADC_FAIL_REPLY_REGEX; 498 } 499 log.debug("Parsed Reply: '{}' length {}", r.toString(), r._nDataChars); 500 return (r); 501 case DCCppConstants.OUTPUT_REPLY: 502 if (s.matches(DCCppConstants.OUTPUT_DEF_REPLY_REGEX)) { 503 r.myRegex = DCCppConstants.OUTPUT_DEF_REPLY_REGEX; 504 } else if (s.matches(DCCppConstants.OUTPUT_REPLY_REGEX)) { 505 r.myRegex = DCCppConstants.OUTPUT_REPLY_REGEX; 506 } 507 log.debug("Parsed Reply: '{}' length {}", r, r._nDataChars); 508 return (r); 509 case DCCppConstants.PROGRAM_REPLY: 510 if (s.matches(DCCppConstants.PROGRAM_BIT_REPLY_REGEX)) { 511 log.debug("Matches ProgBitReply"); 512 r.myRegex = DCCppConstants.PROGRAM_BIT_REPLY_REGEX; 513 } else if (s.matches(DCCppConstants.PROGRAM_BIT_REPLY_V4_REGEX)) { 514 log.debug("Matches ProgBitReplyV2"); 515 r.myRegex = DCCppConstants.PROGRAM_BIT_REPLY_V4_REGEX; 516 } else if (s.matches(DCCppConstants.PROGRAM_REPLY_REGEX)) { 517 log.debug("Matches ProgReply"); 518 r.myRegex = DCCppConstants.PROGRAM_REPLY_REGEX; 519 } else if (s.matches(DCCppConstants.PROGRAM_REPLY_V4_REGEX)) { 520 log.debug("Matches ProgReplyV2"); 521 r.myRegex = DCCppConstants.PROGRAM_REPLY_V4_REGEX; 522 } else if (s.matches(DCCppConstants.PROGRAM_LOCOID_REPLY_REGEX)) { 523 log.debug("Matches ProgLocoIDReply"); 524 r.myRegex = DCCppConstants.PROGRAM_LOCOID_REPLY_REGEX; 525 } else { 526 log.debug("Does not match ProgReply Regex"); 527 } 528 return (r); 529 case DCCppConstants.VERIFY_REPLY: 530 if (s.matches(DCCppConstants.PROGRAM_VERIFY_REPLY_REGEX)) { 531 log.debug("Matches VerifyReply"); 532 r.myRegex = DCCppConstants.PROGRAM_VERIFY_REPLY_REGEX; 533 } else { 534 log.debug("Does not match VerifyReply Regex"); 535 } 536 return (r); 537 case DCCppConstants.POWER_REPLY: 538 if (s.matches(DCCppConstants.TRACK_POWER_REPLY_NAMED_REGEX)) { 539 r.myRegex = DCCppConstants.TRACK_POWER_REPLY_NAMED_REGEX; 540 } else if (s.matches(DCCppConstants.TRACK_POWER_REPLY_REGEX)) { 541 r.myRegex = DCCppConstants.TRACK_POWER_REPLY_REGEX; 542 } 543 return (r); 544 case DCCppConstants.CURRENT_REPLY: 545 if (s.matches(DCCppConstants.CURRENT_REPLY_NAMED_REGEX)) { 546 r.myRegex = DCCppConstants.CURRENT_REPLY_NAMED_REGEX; 547 } else if (s.matches(DCCppConstants.CURRENT_REPLY_REGEX)) { 548 r.myRegex = DCCppConstants.CURRENT_REPLY_REGEX; 549 } 550 return (r); 551 case DCCppConstants.METER_REPLY: 552 if (s.matches(DCCppConstants.METER_REPLY_REGEX)) { 553 r.myRegex = DCCppConstants.METER_REPLY_REGEX; 554 } 555 return (r); 556 case DCCppConstants.MAXNUMSLOTS_REPLY: 557 if (s.matches(DCCppConstants.MAXNUMSLOTS_REPLY_REGEX)) { 558 r.myRegex = DCCppConstants.MAXNUMSLOTS_REPLY_REGEX; 559 } 560 return (r); 561 case DCCppConstants.DIAG_REPLY: 562 if (s.matches(DCCppConstants.DIAG_REPLY_REGEX)) { 563 r.myRegex = DCCppConstants.DIAG_REPLY_REGEX; 564 } 565 return (r); 566 case DCCppConstants.LCD_TEXT_REPLY: 567 if (s.matches(DCCppConstants.LCD_TEXT_REPLY_REGEX)) { 568 r.myRegex = DCCppConstants.LCD_TEXT_REPLY_REGEX; 569 } 570 return (r); 571 case DCCppConstants.WRITE_EEPROM_REPLY: 572 if (s.matches(DCCppConstants.WRITE_EEPROM_REPLY_REGEX)) { 573 r.myRegex = DCCppConstants.WRITE_EEPROM_REPLY_REGEX; 574 } 575 return (r); 576 case DCCppConstants.SENSOR_REPLY_H: 577 if (s.matches(DCCppConstants.SENSOR_INACTIVE_REPLY_REGEX)) { 578 r.myRegex = DCCppConstants.SENSOR_INACTIVE_REPLY_REGEX; 579 } 580 return (r); 581 case DCCppConstants.SENSOR_REPLY_L: 582 if (s.matches(DCCppConstants.SENSOR_DEF_REPLY_REGEX)) { 583 r.myRegex = DCCppConstants.SENSOR_DEF_REPLY_REGEX; 584 } else if (s.matches(DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX)) { 585 r.myRegex = DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX; 586 } 587 return (r); 588 case DCCppConstants.MADC_FAIL_REPLY: 589 r.myRegex = DCCppConstants.MADC_FAIL_REPLY_REGEX; 590 return (r); 591 case DCCppConstants.MADC_SUCCESS_REPLY: 592 r.myRegex = DCCppConstants.MADC_SUCCESS_REPLY_REGEX; 593 return (r); 594 case DCCppConstants.COMM_TYPE_REPLY: 595 r.myRegex = DCCppConstants.COMM_TYPE_REPLY_REGEX; 596 return (r); 597 case DCCppConstants.LOCO_STATE_REPLY: 598 r.myRegex = DCCppConstants.LOCO_STATE_REGEX; 599 return (r); 600 case DCCppConstants.THROTTLE_COMMANDS_REPLY: 601 if (s.matches(DCCppConstants.TURNOUT_IDS_REPLY_REGEX)) { 602 r.myRegex = DCCppConstants.TURNOUT_IDS_REPLY_REGEX; 603 } else if (s.matches(DCCppConstants.TURNOUT_ID_REPLY_REGEX)) { 604 r.myRegex = DCCppConstants.TURNOUT_ID_REPLY_REGEX; 605 } else if (s.matches(DCCppConstants.ROSTER_IDS_REPLY_REGEX)) { 606 r.myRegex = DCCppConstants.ROSTER_IDS_REPLY_REGEX; 607 } else if (s.matches(DCCppConstants.ROSTER_ID_REPLY_REGEX)) { 608 r.myRegex = DCCppConstants.ROSTER_ID_REPLY_REGEX; 609 } else if (s.matches(DCCppConstants.AUTOMATION_IDS_REPLY_REGEX)) { 610 r.myRegex = DCCppConstants.AUTOMATION_IDS_REPLY_REGEX; 611 } else if (s.matches(DCCppConstants.AUTOMATION_ID_REPLY_REGEX)) { 612 r.myRegex = DCCppConstants.AUTOMATION_ID_REPLY_REGEX; 613 } else if (s.matches(DCCppConstants.CURRENT_MAXES_REPLY_REGEX)) { 614 r.myRegex = DCCppConstants.CURRENT_MAXES_REPLY_REGEX; 615 } else if (s.matches(DCCppConstants.CURRENT_VALUES_REPLY_REGEX)) { 616 r.myRegex = DCCppConstants.CURRENT_VALUES_REPLY_REGEX; 617 } else if (s.matches(DCCppConstants.CLOCK_REPLY_REGEX)) { 618 r.myRegex = DCCppConstants.CLOCK_REPLY_REGEX; 619 } 620 log.debug("Parsed Reply: '{}' length {}", r.toString(), r._nDataChars); 621 return (r); 622 case DCCppConstants.TRACKMANAGER_CMD: 623 if (s.matches(DCCppConstants.TRACKMANAGER_REPLY_REGEX)) { 624 r.myRegex = DCCppConstants.TRACKMANAGER_REPLY_REGEX; 625 } 626 return (r); 627 default: 628 return (r); 629 } 630 } 631 632 /** 633 * Not really used inside of DCC++. Just here to play nicely with the 634 * inheritance. 635 * TODO: If this is unused, can we just not override it and (not) "use" 636 * the superclass version? 637 * ANSWER: No, we can't because the superclass looks in the _datachars 638 * element, which we don't use, and which will contain garbage data. 639 * Better to return something meaningful. 640 * 641 * @return first char of myReply as integer 642 */ 643 @Override 644 public int getOpCode() { 645 if (myReply.length() > 0) { 646 return (Character.getNumericValue(myReply.charAt(0))); 647 } else { 648 return (0); 649 } 650 } 651 652 /** 653 * Get the opcode as a one character string. 654 * 655 * @return first char of myReply 656 */ 657 public char getOpCodeChar() { 658 if (myReply.length() > 0) { 659 return (myReply.charAt(0)); 660 } else { 661 return (0); 662 } 663 } 664 665 @Override 666 public int getElement(int n) { 667 if ((n >= 0) && (n < myReply.length())) { 668 return (myReply.charAt(n)); 669 } else { 670 return (' '); 671 } 672 } 673 674 @Override 675 public void setElement(int n, int v) { 676 // We want the ASCII value, not the string interpretation of the int 677 char c = (char) (v & 0xFF); 678 if (myReply == null) { 679 myReply = new StringBuilder(Character.toString(c)); 680 } else if (n >= myReply.length()) { 681 myReply.append(c); 682 } else if (n > 0) { 683 myReply.setCharAt(n, c); 684 } 685 } 686 687 public boolean getValueBool(int idx) { 688 Matcher m = DCCppReply.match(myReply.toString(), myRegex, "gvb"); 689 if (m == null) { 690 log.error("DCCppReply '{}' not matched by '{}'", this.toString(), myRegex); 691 return (false); 692 } else if (idx <= m.groupCount()) { 693 return (!m.group(idx).equals("0")); 694 } else { 695 log.error("DCCppReply bool value index too big. idx = {} msg = {}", idx, this.toString()); 696 return (false); 697 } 698 } 699 700 public String getValueString(int idx) { 701 Matcher m = DCCppReply.match(myReply.toString(), myRegex, "gvs"); 702 if (m == null) { 703 log.error("DCCppReply '{}' not matched by '{}'", this.toString(), myRegex); 704 return (""); 705 } else if (idx <= m.groupCount()) { 706 return (m.group(idx)); 707 } else { 708 log.error("DCCppReply string value index too big. idx = {} msg = {}", idx, this.toString()); 709 return (""); 710 } 711 } 712 713 // is there a match at idx? 714 public boolean valueExists(int idx) { 715 Matcher m = DCCppReply.match(myReply.toString(), myRegex, "gvs"); 716 return (m != null) && (idx <= m.groupCount()); 717 } 718 719 public int getValueInt(int idx) { 720 Matcher m = DCCppReply.match(myReply.toString(), myRegex, "gvi"); 721 if (m == null) { 722 log.error("DCCppReply '{}' not matched by '{}'", this.toString(), myRegex); 723 return (0); 724 } else if (idx <= m.groupCount()) { 725 return (Integer.parseInt(m.group(idx))); 726 } else { 727 log.error("DCCppReply int value index too big. idx = {} msg = {}", idx, this.toString()); 728 return (0); 729 } 730 } 731 732 public double getValueDouble(int idx) { 733 Matcher m = DCCppReply.match(myReply.toString(), myRegex, "gvd"); 734 if (m == null) { 735 log.error("DCCppReply '{}' not matched by '{}'", this.toString(), myRegex); 736 return (0.0); 737 } else if (idx <= m.groupCount()) { 738 return (Double.parseDouble(m.group(idx))); 739 } else { 740 log.error("DCCppReply double value index too big. idx = {} msg = {}", idx, this.toString()); 741 return (0.0); 742 } 743 } 744 745 /* 746 * skipPrefix is not used at this point in time, but is defined as abstract 747 * in AbstractMRReply 748 */ 749 @Override 750 protected int skipPrefix(int index) { 751 return -1; 752 } 753 754 @Override 755 public int maxSize() { 756 return DCCppConstants.MAX_REPLY_SIZE; 757 } 758 759 public int getLength() { 760 return (myReply.length()); 761 } 762 763 /* 764 * Some notes on DCC++ messages and responses... 765 * 766 * Messages that have responses expected: 767 * t : <T REGISTER SPEED DIRECTION> 768 * f : (none) 769 * a : (none) 770 * T : <H ID THROW> 771 * w : (none) 772 * b : (none) 773 * W : <r CALLBACKNUM|CALLBACKSUB|CV CV_Value> 774 * B : <r CALLBACKNUM CALLBACKSUB|CV|Bit CV_Bit_Value> 775 * R : <r CALLBACKNUM|CALLBACKSUB|CV CV_Value> 776 * 1 : <p1> 777 * 0 : <p0> 778 * c : <a CURRENT> 779 * s : Series of status messages... 780 * <p[0,1]> Power state 781 * <T ...>Throttle responses from all registers 782 * <iDCC++ ... > Base station version and build date 783 * <H ID ADDR INDEX THROW> All turnout states. 784 * 785 * Unsolicited Replies: 786 * <Q snum [0,1]> Sensor reply. 787 * Debug messages: 788 * M : (none) 789 * P : (none) 790 * f : <f MEM> 791 * L : <M ... data ... > 792 */ 793 794 // ------------------------------------------------------------------- 795 // Message helper functions 796 // Core methods 797 798 protected boolean matches(String pat) { 799 return (match(this.toString(), pat, "Validator") != null); 800 } 801 802 protected static Matcher match(String s, String pat, String name) { 803 try { 804 Pattern p = Pattern.compile(pat); 805 Matcher m = p.matcher(s); 806 if (!m.matches()) { 807 log.trace("No Match {} Command: {} pattern {}", name, s, pat); 808 return (null); 809 } 810 return (m); 811 812 } catch (PatternSyntaxException e) { 813 log.error("Malformed DCC++ reply syntax! s = {}", pat); 814 return (null); 815 } catch (IllegalStateException e) { 816 log.error("Group called before match operation executed string = {}", s); 817 return (null); 818 } catch (IndexOutOfBoundsException e) { 819 log.error("Index out of bounds string = {}", s); 820 return (null); 821 } 822 } 823 824 public String getStationType() { 825 if (this.isStatusReply()) { 826 return (this.getValueString(1)); // 1st match in all versions 827 } else { 828 return ("Unknown"); 829 } 830 } 831 832 // build value is 3rd match in v3+, 2nd in previous 833 public String getBuildString() { 834 if (this.isStatusReply()) { 835 if (this.valueExists(3)) { 836 return(this.getValueString(3)); 837 } else { 838 return(this.getValueString(2)); 839 } 840 } else { 841 return("Unknown"); 842 } 843 } 844 845 // look for canonical version in 2nd match 846 public String getVersion() { 847 if (this.isStatusReply()) { 848 String s = this.getValueString(2); 849 if (jmri.Version.isCanonicalVersion(s)) { 850 return s; 851 } else { 852 return ("0.0.0"); 853 } 854 } else { 855 return ("Unknown"); 856 } 857 } 858 859 // Helper methods for Throttle and LocoState Replies 860 861 public int getLocoIdInt() { 862 if (this.isLocoStateReply() || this.isProgramLocoIdReply()) { 863 return (this.getValueInt(1)); 864 } else { 865 log.error("LocoId Parser called on non-LocoId message type {}", this.getOpCodeChar()); 866 return (-1); 867 } 868 } 869 870 public int getSpeedByteInt() { 871 if (this.isLocoStateReply()) { 872 return (this.getValueInt(3)); 873 } else { 874 log.error("ThrottleReply Parser called on non-Throttle message type {}", this.getOpCodeChar()); 875 return (0); 876 } 877 } 878 879 public int getFunctionsInt() { 880 if (this.isLocoStateReply()) { 881 return (this.getValueInt(4)); 882 } else { 883 log.error("ThrottleReply Parser called on non-Throttle message type {}", this.getOpCodeChar()); 884 return (0); 885 } 886 } 887 888 public String getFunctionsString() { 889 if (this.isLocoStateReply()) { 890 return new StringBuilder(StringUtils.leftPad(Integer.toBinaryString(this.getValueInt(4)), 29, "0")) 891 .reverse().toString(); 892 } else { 893 log.error("ThrottleReply Parser called on non-Throttle message type {}", this.getOpCodeChar()); 894 return ("not a locostate!"); 895 } 896 } 897 898 public String getRegisterString() { 899 return String.valueOf(getRegisterInt()); 900 } 901 902 public int getRegisterInt() { 903 if (this.isThrottleReply()) { 904 return (this.getValueInt(1)); 905 } else { 906 log.error("ThrottleReply Parser called on non-Throttle message type {}", this.getOpCodeChar()); 907 return (0); 908 } 909 } 910 911 public String getSpeedString() { 912 return String.valueOf(getSpeedInt()); 913 } 914 915 public int getSpeedInt() { 916 if (this.isThrottleReply()) { 917 return (this.getValueInt(2)); 918 } else if (this.isLocoStateReply()) { 919 int speed = this.getValueInt(3) & 0x7f; // drop direction bit 920 if (speed == 1) { 921 return -1; // special case for eStop 922 } 923 if (speed > 1) { 924 return speed - 1; // bump speeds down 1 due to eStop at 1 925 } 926 return 0; // stop is zero 927 } else { 928 log.error("ThrottleReply Parser called on non-Throttle message type {}", this.getOpCodeChar()); 929 return (0); 930 } 931 } 932 933 public boolean isEStop() { 934 return getSpeedInt() == -1; 935 } 936 937 public String getDirectionString() { 938 // Will return "Forward" (true) or "Reverse" (false) 939 return (getDirectionInt() == 1 ? "Forward" : "Reverse"); 940 } 941 942 public int getDirectionInt() { 943 // Will return 1 (true) or 0 (false) 944 if (this.isThrottleReply()) { 945 return (this.getValueInt(3)); 946 } else if (this.isLocoStateReply()) { 947 return this.getValueInt(3) >> 7; 948 } else { 949 log.error("ThrottleReply Parser called on non-ThrottleReply message type {}", this.getOpCodeChar()); 950 return (0); 951 } 952 } 953 954 public boolean getDirectionBool() { 955 // Will return true or false 956 if (this.isThrottleReply()) { 957 return (this.getValueBool(3)); 958 } else if (this.isLocoStateReply()) { 959 return ((this.getValueInt(3) >> 7) == 1); 960 } else { 961 log.error("ThrottleReply Parser called on non-ThrottleReply message type {}", this.getOpCodeChar()); 962 return (false); 963 } 964 } 965 966 public boolean getIsForward() { 967 return getDirectionBool(); 968 } 969 970 // ------------------------------------------------------ 971 // Helper methods for Turnout Replies 972 973 public String getTOIDString() { 974 if (this.isTurnoutReply() || this.isTurnoutIDReply()) { 975 return (this.getValueString(1)); 976 } else { 977 log.error("TurnoutReply Parser called on non-TurnoutReply message type {}", this.getOpCodeChar()); 978 return ("0"); 979 } 980 } 981 public int getTOIDInt() { 982 if (this.isTurnoutReply() || this.isTurnoutIDReply()) { 983 return (this.getValueInt(1)); 984 } else { 985 log.error("TurnoutReply Parser called on non-TurnoutReply message type {}", this.getOpCodeChar()); 986 return (0); 987 } 988 } 989 990 public String getTOAddressString() { 991 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { 992 return (this.getValueString(2)); 993 } else { 994 return ("-1"); 995 } 996 } 997 998 public int getTOAddressInt() { 999 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { 1000 return (this.getValueInt(2)); 1001 } else { 1002 return (-1); 1003 } 1004 } 1005 1006 public String getTOAddressIndexString() { 1007 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { 1008 return (this.getValueString(3)); 1009 } else { 1010 return ("-1"); 1011 } 1012 } 1013 1014 public int getTOAddressIndexInt() { 1015 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { 1016 return (this.getValueInt(3)); 1017 } else { 1018 return (-1); 1019 } 1020 } 1021 1022 public int getTOPinInt() { 1023 if (this.isTurnoutDefServoReply() || this.isTurnoutDefVpinReply()) { 1024 return (this.getValueInt(2)); 1025 } else { 1026 return (-1); 1027 } 1028 } 1029 1030 public int getTOThrownPositionInt() { 1031 if (this.isTurnoutDefServoReply()) { 1032 return (this.getValueInt(3)); 1033 } else { 1034 return (-1); 1035 } 1036 } 1037 1038 public int getTOClosedPositionInt() { 1039 if (this.isTurnoutDefServoReply()) { 1040 return (this.getValueInt(4)); 1041 } else { 1042 return (-1); 1043 } 1044 } 1045 1046 public int getTOProfileInt() { 1047 if (this.isTurnoutDefServoReply()) { 1048 return (this.getValueInt(5)); 1049 } else { 1050 return (-1); 1051 } 1052 } 1053 1054 public String getTOStateString() { 1055 // Will return human readable state. To get string value for command, 1056 // use 1057 // getTOStateInt().toString() 1058 if (this.isTurnoutReply()) { 1059 return (this.getTOStateInt() == 1 ? "THROWN" : "CLOSED"); 1060 } else { 1061 log.error("TurnoutReply Parser called on non-TurnoutReply message type {}", this.getOpCodeChar()); 1062 return ("Not a Turnout"); 1063 } 1064 } 1065 1066 public int getTOStateInt() { 1067 // Will return 1 (true - thrown) or 0 (false - closed) 1068 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { // turnout 1069 // list 1070 // response 1071 return (this.getValueInt(4)); 1072 } else if (this.isTurnoutDefServoReply()) { // servo turnout 1073 return (this.getValueInt(6)); 1074 } else if (this.isTurnoutDefVpinReply()) { // vpin turnout 1075 return (this.getValueInt(3)); 1076 } else if (this.isTurnoutDefLCNReply()) { // LCN turnout 1077 return (this.getValueInt(2)); 1078 } else if (this.isTurnoutReply()) { // single turnout response 1079 return (this.getValueInt(2)); 1080 } else { 1081 log.error("TOStateInt Parser called on non-TOStateInt message type {}", this.getOpCodeChar()); 1082 return (0); 1083 } 1084 } 1085 1086 public boolean getTOIsThrown() { 1087 return (this.getTOStateInt() == 1); 1088 } 1089 1090 public boolean getTOIsClosed() { 1091 return (!this.getTOIsThrown()); 1092 } 1093 1094 public String getRosterIDString() { 1095 return (Integer.toString(this.getRosterIDInt())); 1096 } 1097 public int getRosterIDInt() { 1098 if (this.isRosterIDReply()) { 1099 return (this.getValueInt(1)); 1100 } else { 1101 log.error("RosterIDInt Parser called on non-RosterIDInt message type {}", this.getOpCodeChar()); 1102 return (0); 1103 } 1104 } 1105 public String getAutomationIDString() { 1106 return (Integer.toString(this.getAutomationIDInt())); 1107 } 1108 public int getAutomationIDInt() { 1109 if (this.isAutomationIDReply()) { 1110 return (this.getValueInt(1)); 1111 } else { 1112 log.error("AutomationIDInt Parser called on non-AutomationIDInt message type {}", this.getOpCodeChar()); 1113 return (0); 1114 } 1115 } 1116 1117 // ------------------------------------------------------ 1118 // Helper methods for Program Replies 1119 1120 public String getCallbackNumString() { 1121 if (this.isProgramReply() || isProgramBitReply()) { 1122 return (this.getValueString(1)); 1123 } else { 1124 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1125 return ("0"); 1126 } 1127 } 1128 1129 public int getCallbackNumInt() { 1130 if (this.isProgramReply() || isProgramBitReply() ) { 1131 return(this.getValueInt(1)); 1132 } else { 1133 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1134 return(0); 1135 } 1136 } 1137 1138 public String getCallbackSubString() { 1139 if (this.isProgramReply() || isProgramBitReply()) { 1140 return (this.getValueString(2)); 1141 } else { 1142 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1143 return ("0"); 1144 } 1145 } 1146 1147 public int getCallbackSubInt() { 1148 if (this.isProgramReply() || isProgramBitReply()) { 1149 return (this.getValueInt(2)); 1150 } else { 1151 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1152 return (0); 1153 } 1154 } 1155 1156 public String getCVString() { 1157 if (this.isProgramReply()) { 1158 return (this.getValueString(3)); 1159 } else if (this.isProgramBitReply()) { 1160 return (this.getValueString(3)); 1161 } else if (this.isVerifyReply()) { 1162 return (this.getValueString(1)); 1163 } else if (this.isProgramReplyV4()) { 1164 return (this.getValueString(1)); 1165 } else if (this.isProgramBitReplyV4()) { 1166 return (this.getValueString(1)); 1167 } else { 1168 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1169 return ("0"); 1170 } 1171 } 1172 1173 public int getCVInt() { 1174 if (this.isProgramReply()) { 1175 return (this.getValueInt(3)); 1176 } else if (this.isProgramBitReply()) { 1177 return (this.getValueInt(3)); 1178 } else if (this.isVerifyReply()) { 1179 return (this.getValueInt(1)); 1180 } else if (this.isProgramReplyV4()) { 1181 return (this.getValueInt(1)); 1182 } else if (this.isProgramBitReplyV4()) { 1183 return (this.getValueInt(1)); 1184 } else { 1185 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1186 return (0); 1187 } 1188 } 1189 1190 public String getProgramBitString() { 1191 if (this.isProgramBitReply()) { 1192 return (this.getValueString(4)); 1193 } else if (this.isProgramBitReplyV4()) { 1194 return (this.getValueString(2)); 1195 } else { 1196 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1197 return ("0"); 1198 } 1199 } 1200 1201 public int getProgramBitInt() { 1202 if (this.isProgramBitReply()) { 1203 return (this.getValueInt(4)); 1204 } else { 1205 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1206 return (0); 1207 } 1208 } 1209 1210 public String getReadValueString() { 1211 if (this.isProgramReply()) { 1212 return (this.getValueString(4)); 1213 } else if (this.isProgramBitReply()) { 1214 return (this.getValueString(5)); 1215 } else if (this.isProgramBitReplyV4()) { 1216 return (this.getValueString(3)); 1217 } else if (this.isProgramReplyV4()) { 1218 return (this.getValueString(2)); 1219 } else if (this.isVerifyReply()) { 1220 return (this.getValueString(2)); 1221 } else { 1222 log.error("ProgramReply Parser called on non-ProgramReply message type {}", this.getOpCodeChar()); 1223 return ("0"); 1224 } 1225 } 1226 1227 public int getReadValueInt() { 1228 return (Integer.parseInt(this.getReadValueString())); 1229 } 1230 1231 public String getPowerDistrictName() { 1232 if (this.isNamedPowerReply()) { 1233 return (this.getValueString(2)); 1234 } else { 1235 log.error("NamedPowerReply Parser called on non-NamedPowerReply message type '{}' message '{}'", 1236 this.getOpCodeChar(), this.toString()); 1237 return (""); 1238 } 1239 } 1240 1241 public String getPowerDistrictStatus() { 1242 if (this.isNamedPowerReply()) { 1243 switch (this.getValueString(1)) { 1244 case DCCppConstants.POWER_OFF: 1245 return ("OFF"); 1246 case DCCppConstants.POWER_ON: 1247 return ("ON"); 1248 default: 1249 return ("OVERLOAD"); 1250 } 1251 } else { 1252 log.error("NamedPowerReply Parser called on non-NamedPowerReply message type {} message {}", 1253 this.getOpCodeChar(), this.toString()); 1254 return (""); 1255 } 1256 } 1257 1258 public String getCurrentString() { 1259 if (this.isCurrentReply()) { 1260 if (this.isNamedCurrentReply()) { 1261 return (this.getValueString(2)); 1262 } 1263 return (this.getValueString(1)); 1264 } else { 1265 log.error("CurrentReply Parser called on non-CurrentReply message type {} message {}", this.getOpCodeChar(), 1266 this.toString()); 1267 return ("0"); 1268 } 1269 } 1270 1271 public int getCurrentInt() { 1272 return (Integer.parseInt(this.getCurrentString())); 1273 } 1274 1275 public String getMeterName() { 1276 if (this.isMeterReply()) { 1277 return (this.getValueString(1)); 1278 } else { 1279 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1280 this.toString()); 1281 return (""); 1282 } 1283 } 1284 1285 public double getMeterValue() { 1286 if (this.isMeterReply()) { 1287 return (this.getValueDouble(2)); 1288 } else { 1289 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1290 this.toString()); 1291 return (0.0); 1292 } 1293 } 1294 1295 public String getMeterType() { 1296 if (this.isMeterReply()) { 1297 String t = getValueString(3); 1298 if (t.equals(DCCppConstants.VOLTAGE) || t.equals(DCCppConstants.CURRENT)) { 1299 return (t); 1300 } else { 1301 log.warn("Meter Type '{}' is not valid type in message '{}'", t, this.toString()); 1302 return (""); 1303 } 1304 } else { 1305 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1306 this.toString()); 1307 return (""); 1308 } 1309 } 1310 1311 public jmri.Meter.Unit getMeterUnit() { 1312 if (this.isMeterReply()) { 1313 String us = this.getValueString(4); 1314 AbstractXmlAdapter.EnumIO<jmri.Meter.Unit> map = 1315 new AbstractXmlAdapter.EnumIoNames<>(jmri.Meter.Unit.class); 1316 return (map.inputFromString(us)); 1317 } else { 1318 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1319 this.toString()); 1320 return (jmri.Meter.Unit.NoPrefix); 1321 } 1322 } 1323 1324 public double getMeterMinValue() { 1325 if (this.isMeterReply()) { 1326 return (this.getValueDouble(5)); 1327 } else { 1328 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1329 this.toString()); 1330 return (0.0); 1331 } 1332 } 1333 1334 public double getMeterMaxValue() { 1335 if (this.isMeterReply()) { 1336 return (this.getValueDouble(6)); 1337 } else { 1338 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1339 this.toString()); 1340 return (0.0); 1341 } 1342 } 1343 1344 public double getMeterResolution() { 1345 if (this.isMeterReply()) { 1346 return (this.getValueDouble(7)); 1347 } else { 1348 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1349 this.toString()); 1350 return (0.0); 1351 } 1352 } 1353 1354 public double getMeterWarnValue() { 1355 if (this.isMeterReply()) { 1356 return (this.getValueDouble(8)); 1357 } else { 1358 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1359 this.toString()); 1360 return (0.0); 1361 } 1362 } 1363 1364 public boolean isMeterTypeVolt() { 1365 if (this.isMeterReply()) { 1366 return (this.getMeterType().equals(DCCppConstants.VOLTAGE)); 1367 } else { 1368 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1369 this.toString()); 1370 return (false); 1371 } 1372 } 1373 1374 public boolean isMeterTypeCurrent() { 1375 if (this.isMeterReply()) { 1376 return (this.getMeterType().equals(DCCppConstants.CURRENT)); 1377 } else { 1378 log.error("MeterReply Parser called on non-MeterReply message type '{}' message '{}'", this.getOpCodeChar(), 1379 this.toString()); 1380 return (false); 1381 } 1382 } 1383 1384 public boolean getPowerBool() { 1385 if (this.isPowerReply()) { 1386 return (this.getValueString(1).equals(DCCppConstants.POWER_ON)); 1387 } else { 1388 log.error("PowerReply Parser called on non-PowerReply message type {} message {}", this.getOpCodeChar(), 1389 this.toString()); 1390 return (false); 1391 } 1392 } 1393 1394 public String getTurnoutDefNumString() { 1395 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { 1396 return (this.getValueString(1)); 1397 } else { 1398 log.error("TurnoutDefReply Parser called on non-TurnoutDefReply message type {}", this.getOpCodeChar()); 1399 return ("0"); 1400 } 1401 } 1402 1403 public int getTurnoutDefNumInt() { 1404 return (Integer.parseInt(this.getTurnoutDefNumString())); 1405 } 1406 1407 public String getTurnoutDefAddrString() { 1408 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { 1409 return (this.getValueString(2)); 1410 } else { 1411 log.error("TurnoutDefReply Parser called on non-TurnoutDefReply message type {}", this.getOpCodeChar()); 1412 return ("0"); 1413 } 1414 } 1415 1416 public int getTurnoutDefAddrInt() { 1417 return (Integer.parseInt(this.getTurnoutDefAddrString())); 1418 } 1419 1420 public String getTurnoutDefSubAddrString() { 1421 if (this.isTurnoutDefReply() || this.isTurnoutDefDCCReply()) { 1422 return (this.getValueString(3)); 1423 } else { 1424 log.error("TurnoutDefReply Parser called on non-TurnoutDefReply message type {}", this.getOpCodeChar()); 1425 return ("0"); 1426 } 1427 } 1428 1429 public int getTurnoutDefSubAddrInt() { 1430 return (Integer.parseInt(this.getTurnoutDefSubAddrString())); 1431 } 1432 1433 public String getOutputNumString() { 1434 if (this.isOutputDefReply() || this.isOutputCmdReply()) { 1435 return (this.getValueString(1)); 1436 } else { 1437 log.error("OutputAddReply Parser called on non-OutputAddReply message type {}", this.getOpCodeChar()); 1438 return ("0"); 1439 } 1440 } 1441 1442 public int getOutputNumInt() { 1443 return (Integer.parseInt(this.getOutputNumString())); 1444 } 1445 1446 public String getOutputListPinString() { 1447 if (this.isOutputDefReply()) { 1448 return (this.getValueString(2)); 1449 } else { 1450 log.error("OutputAddReply Parser called on non-OutputAddReply message type {}", this.getOpCodeChar()); 1451 return ("0"); 1452 } 1453 } 1454 1455 public int getOutputListPinInt() { 1456 return (Integer.parseInt(this.getOutputListPinString())); 1457 } 1458 1459 public String getOutputListIFlagString() { 1460 if (this.isOutputDefReply()) { 1461 return (this.getValueString(3)); 1462 } else { 1463 log.error("OutputListReply Parser called on non-OutputListReply message type {}", this.getOpCodeChar()); 1464 return ("0"); 1465 } 1466 } 1467 1468 public int getOutputListIFlagInt() { 1469 return (Integer.parseInt(this.getOutputListIFlagString())); 1470 } 1471 1472 public String getOutputListStateString() { 1473 if (this.isOutputDefReply()) { 1474 return (this.getValueString(4)); 1475 } else { 1476 log.error("OutputListReply Parser called on non-OutputListReply message type {}", this.getOpCodeChar()); 1477 return ("0"); 1478 } 1479 } 1480 1481 public int getOutputListStateInt() { 1482 return (Integer.parseInt(this.getOutputListStateString())); 1483 } 1484 1485 public boolean getOutputCmdStateBool() { 1486 if (this.isOutputCmdReply()) { 1487 return ((this.getValueBool(2))); 1488 } else { 1489 log.error("OutputCmdReply Parser called on non-OutputCmdReply message type {}", this.getOpCodeChar()); 1490 return (false); 1491 } 1492 } 1493 1494 public String getOutputCmdStateString() { 1495 if (this.isOutputCmdReply()) { 1496 return (this.getValueBool(2) ? "HIGH" : "LOW"); 1497 } else { 1498 log.error("OutputCmdReply Parser called on non-OutputCmdReply message type {}", this.getOpCodeChar()); 1499 return ("0"); 1500 } 1501 } 1502 1503 public int getOutputCmdStateInt() { 1504 return (this.getOutputCmdStateBool() ? 1 : 0); 1505 } 1506 1507 public boolean getOutputIsHigh() { 1508 return (this.getOutputCmdStateBool()); 1509 } 1510 1511 public boolean getOutputIsLow() { 1512 return (!this.getOutputCmdStateBool()); 1513 } 1514 1515 public String getSensorDefNumString() { 1516 if (this.isSensorDefReply()) { 1517 return (this.getValueString(1)); 1518 } else { 1519 log.error("SensorDefReply Parser called on non-SensorDefReply message type {}", this.getOpCodeChar()); 1520 return ("0"); 1521 } 1522 } 1523 1524 public int getSensorDefNumInt() { 1525 return (Integer.parseInt(this.getSensorDefNumString())); 1526 } 1527 1528 public String getSensorDefPinString() { 1529 if (this.isSensorDefReply()) { 1530 return (this.getValueString(2)); 1531 } else { 1532 log.error("SensorDefReply Parser called on non-SensorDefReply message type {}", this.getOpCodeChar()); 1533 return ("0"); 1534 } 1535 } 1536 1537 public int getSensorDefPinInt() { 1538 return (Integer.parseInt(this.getSensorDefPinString())); 1539 } 1540 1541 public String getSensorDefPullupString() { 1542 if (this.isSensorDefReply()) { 1543 return (this.getSensorDefPullupBool() ? "Pullup" : "NoPullup"); 1544 } else { 1545 log.error("SensorDefReply Parser called on non-SensorDefReply message type {}", this.getOpCodeChar()); 1546 return ("Not a Sensor"); 1547 } 1548 } 1549 1550 public int getSensorDefPullupInt() { 1551 if (this.isSensorDefReply()) { 1552 return (this.getValueInt(3)); 1553 } else { 1554 log.error("SensorDefReply Parser called on non-SensorDefReply message type {}", this.getOpCodeChar()); 1555 return (0); 1556 } 1557 } 1558 1559 public boolean getSensorDefPullupBool() { 1560 if (this.isSensorDefReply()) { 1561 return (this.getValueBool(3)); 1562 } else { 1563 log.error("SensorDefReply Parser called on non-SensorDefReply message type {}", this.getOpCodeChar()); 1564 return (false); 1565 } 1566 } 1567 1568 public String getSensorNumString() { 1569 if (this.isSensorReply()) { 1570 return (this.getValueString(1)); 1571 } else { 1572 log.error("SensorReply Parser called on non-SensorReply message type {}", this.getOpCodeChar()); 1573 return ("0"); 1574 } 1575 } 1576 1577 public int getSensorNumInt() { 1578 return (Integer.parseInt(this.getSensorNumString())); 1579 } 1580 1581 public String getSensorStateString() { 1582 if (this.isSensorReply()) { 1583 return (this.myRegex.equals(DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX) ? "Active" : "Inactive"); 1584 } else { 1585 log.error("SensorReply Parser called on non-SensorReply message type {}", this.getOpCodeChar()); 1586 return ("Not a Sensor"); 1587 } 1588 } 1589 1590 public int getSensorStateInt() { 1591 if (this.isSensorReply()) { 1592 return (this.myRegex.equals(DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX) ? 1 : 0); 1593 } else { 1594 log.error("SensorReply Parser called on non-SensorReply message type {}", this.getOpCodeChar()); 1595 return (0); 1596 } 1597 } 1598 1599 public boolean getSensorIsActive() { 1600 return (this.myRegex.equals(DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX)); 1601 } 1602 1603 public boolean getSensorIsInactive() { 1604 return (this.myRegex.equals(DCCppConstants.SENSOR_INACTIVE_REPLY_REGEX)); 1605 } 1606 1607 public int getCommTypeInt() { 1608 if (this.isCommTypeReply()) { 1609 return (this.getValueInt(1)); 1610 } else { 1611 log.error("CommTypeReply Parser called on non-CommTypeReply message type {}", this.getOpCodeChar()); 1612 return (0); 1613 } 1614 } 1615 1616 public String getCommTypeValueString() { 1617 if (this.isCommTypeReply()) { 1618 return (this.getValueString(2)); 1619 } else { 1620 log.error("CommTypeReply Parser called on non-CommTypeReply message type {}", this.getOpCodeChar()); 1621 return ("N/A"); 1622 } 1623 } 1624 1625 public ArrayList<Integer> getTurnoutIDList() { 1626 ArrayList<Integer> ids=new ArrayList<Integer>(); 1627 if (this.isTurnoutIDsReply()) { 1628 String idList = this.getValueString(1); 1629 if (!idList.isEmpty()) { 1630 String[] idStrings = idList.split(" "); 1631 for (String idString : idStrings) { 1632 ids.add(Integer.parseInt(idString)); 1633 } 1634 } 1635 } else { 1636 log.error("TurnoutIDsReply Parser called on non-TurnoutIDsReply message type {}", this.getOpCodeChar()); 1637 } 1638 return ids; 1639 } 1640 public String getTurnoutStateString() { 1641 if (this.isTurnoutIDReply()) { 1642 return (this.getValueString(2)); 1643 } else { 1644 log.error("getTurnoutStateString Parser called on non-TurnoutID message type {}", this.getOpCodeChar()); 1645 return ("0"); 1646 } 1647 } 1648 public String getTurnoutDescString() { 1649 if (this.isTurnoutIDReply()) { 1650 return (this.getValueString(3)); 1651 } else { 1652 log.error("getTurnoutDescString Parser called on non-TurnoutID message type {}", this.getOpCodeChar()); 1653 return ("0"); 1654 } 1655 } 1656 public String getRosterDescString() { 1657 if (this.isRosterIDReply()) { 1658 return (this.getValueString(2)); 1659 } else { 1660 log.error("getRosterDescString called on non-RosterIDReply message type {}", this.getOpCodeChar()); 1661 return (""); 1662 } 1663 } 1664 public String getRosterFKeysString() { 1665 if (this.isRosterIDReply()) { 1666 return (this.getValueString(3)); 1667 } else { 1668 log.error("getRosterFKeysString called on non-RosterIDReply message type {}", this.getOpCodeChar()); 1669 return (""); 1670 } 1671 } 1672 public ArrayList<Integer> getRosterIDList() { 1673 ArrayList<Integer> ids=new ArrayList<Integer>(); 1674 if (this.isRosterIDsReply()) { 1675 String idList = this.getValueString(1); 1676 if (!idList.isEmpty()) { 1677 String[] idStrings = idList.split(" "); 1678 for (String idString : idStrings) { 1679 ids.add(Integer.parseInt(idString)); 1680 } 1681 } 1682 } else { 1683 log.error("getRosterIDList called on non-RosterIDsReply message type {}", this.getOpCodeChar()); 1684 } 1685 return ids; 1686 } 1687 1688 public String getAutomationTypeString() { 1689 if (this.isAutomationIDReply()) { 1690 return (this.getValueString(2)); 1691 } else { 1692 log.error("getAutomationTypeString called on non-AutomationIDReply message type {}", this.getOpCodeChar()); 1693 return (""); 1694 } 1695 } 1696 public String getAutomationDescString() { 1697 if (this.isAutomationIDReply()) { 1698 return (this.getValueString(3)); 1699 } else { 1700 log.error("getAutomationDescString called on nonAutomationIDReply message type {}", this.getOpCodeChar()); 1701 return (""); 1702 } 1703 } 1704 public ArrayList<Integer> getAutomationIDList() { 1705 ArrayList<Integer> ids=new ArrayList<Integer>(); 1706 if (this.isAutomationIDsReply()) { 1707 String idList = this.getValueString(1); 1708 if (!idList.isEmpty()) { 1709 String[] idStrings = idList.split(" "); 1710 for (String idString : idStrings) { 1711 ids.add(Integer.parseInt(idString)); 1712 } 1713 } 1714 } else { 1715 log.error("getAutomationIDList called on non-AutomationIDsReply message type {}", this.getOpCodeChar()); 1716 } 1717 return ids; 1718 } 1719 public ArrayList<Integer> getCurrentMaxesList() { 1720 ArrayList<Integer> cml=new ArrayList<Integer>(); 1721 if (this.isCurrentMaxesReply()) { 1722 String sml = this.getValueString(1); 1723 if (!sml.isEmpty()) { 1724 String[] mss = sml.split(" "); 1725 for (String ms : mss) { 1726 cml.add(Integer.parseInt(ms)); 1727 } 1728 } 1729 } else { 1730 log.error("getCurrentMaxesList called on non-CurrentMaxesListReply message type {}", this.getOpCodeChar()); 1731 } 1732 return cml; 1733 } 1734 public ArrayList<Integer> getCurrentValuesList() { 1735 ArrayList<Integer> cvl=new ArrayList<Integer>(); 1736 if (this.isCurrentValuesReply()) { 1737 String svl = this.getValueString(1); 1738 if (!svl.isEmpty()) { 1739 String[] vss = svl.split(" "); 1740 for (String vs : vss) { 1741 cvl.add(Integer.parseInt(vs)); 1742 } 1743 } 1744 } else { 1745 log.error("getCurrentValuesList called on non-CurrentValuesListReply message type {}", this.getOpCodeChar()); 1746 } 1747 return cvl; 1748 } 1749 1750 public char getTrackManagerLetter() { 1751 if (this.isTrackManagerReply()) { 1752 String s = this.getValueString(1); 1753 if (!s.isEmpty()) { 1754 return (s.charAt(0)); //convert to a char 1755 } else { 1756 return ('0'); 1757 } 1758 } else { 1759 log.error("getTrackManagerLetter Parser called on non-TrackManager message type {}", this.getOpCodeChar()); 1760 return ('0'); 1761 } 1762 } 1763 public String getTrackManagerMode() { 1764 if (this.isTrackManagerReply()) { 1765 return (this.getValueString(2)); 1766 } else { 1767 log.error("getTrackManagerMode Parser called on non-TrackManager message type {}", this.getOpCodeChar()); 1768 return ("0"); 1769 } 1770 } 1771 1772 public String getClockMinutesString() { 1773 if (this.isClockReply()) { 1774 return (this.getValueString(1)); 1775 } else { 1776 log.error("getClockTimeString Parser called on non-getClockTimeString message type {}", this.getOpCodeChar()); 1777 return ("0"); 1778 } 1779 } 1780 public int getClockMinutesInt() { 1781 return (Integer.parseInt(this.getClockMinutesString())); 1782 } 1783 public String getClockRateString() { 1784 if (this.isClockReply()) { 1785 return (this.getValueString(2)); 1786 } else { 1787 log.error("getClockRateString Parser called on non-getClockRateString message type {}", this.getOpCodeChar()); 1788 return ("0"); 1789 } 1790 } 1791 public int getClockRateInt() { 1792 return (Integer.parseInt(this.getClockRateString())); 1793 } 1794 1795 // <@ 0 8 "message text"> 1796 public boolean isLCDTextReply() { 1797 return (this.matches(DCCppConstants.LCD_TEXT_REPLY_REGEX)); 1798 } 1799 public String getLCDTextString() { 1800 if (this.isLCDTextReply()) { 1801 return (this.getValueString(3)); 1802 } else { 1803 log.error("getLCDTextString Parser called on non-LCDTextString message type {}", this.getOpCodeChar()); 1804 return ("error"); 1805 } 1806 } 1807 public String getLCDDisplayNumString() { 1808 if (this.isLCDTextReply()) { 1809 return (this.getValueString(1)); 1810 } else { 1811 log.error("getLCDDisplayNumString Parser called on non-LCDTextString message type {}", this.getOpCodeChar()); 1812 return ("error"); 1813 } 1814 } 1815 public int getLCDDisplayNumInt() { 1816 return (Integer.parseInt(this.getLCDDisplayNumString())); 1817 } 1818 public String getLCDLineNumString() { 1819 if (this.isLCDTextReply()) { 1820 return (this.getValueString(2)); 1821 } else { 1822 log.error("getLCDLineNumString Parser called on non-LCDTextString message type {}", this.getOpCodeChar()); 1823 return ("error"); 1824 } 1825 } 1826 public int getLCDLineNumInt() { 1827 return (Integer.parseInt(this.getLCDLineNumString())); 1828 } 1829 1830 // ------------------------------------------------------------------- 1831 1832 // Message Identification functions 1833 public boolean isThrottleReply() { 1834 return (this.getOpCodeChar() == DCCppConstants.THROTTLE_REPLY); 1835 } 1836 1837 public boolean isTurnoutReply() { 1838 return (this.getOpCodeChar() == DCCppConstants.TURNOUT_REPLY); 1839 } 1840 1841 public boolean isTurnoutCmdReply() { 1842 return (this.matches(DCCppConstants.TURNOUT_REPLY_REGEX)); 1843 } 1844 1845 public boolean isProgramReply() { 1846 return (this.matches(DCCppConstants.PROGRAM_REPLY_REGEX)); 1847 } 1848 1849 public boolean isProgramReplyV4() { 1850 return (this.matches(DCCppConstants.PROGRAM_REPLY_V4_REGEX)); 1851 } 1852 1853 public boolean isProgramLocoIdReply() { 1854 return (this.matches(DCCppConstants.PROGRAM_LOCOID_REPLY_REGEX)); 1855 } 1856 1857 public boolean isVerifyReply() { 1858 return (this.matches(DCCppConstants.PROGRAM_VERIFY_REPLY_REGEX)); 1859 } 1860 1861 public boolean isProgramBitReply() { 1862 return (this.matches(DCCppConstants.PROGRAM_BIT_REPLY_REGEX)); 1863 } 1864 1865 public boolean isProgramBitReplyV4() { 1866 return (this.matches(DCCppConstants.PROGRAM_BIT_REPLY_V4_REGEX)); 1867 } 1868 1869 public boolean isPowerReply() { 1870 return (this.getOpCodeChar() == DCCppConstants.POWER_REPLY); 1871 } 1872 1873 public boolean isNamedPowerReply() { 1874 return (this.matches(DCCppConstants.TRACK_POWER_REPLY_NAMED_REGEX)); 1875 } 1876 1877 public boolean isMaxNumSlotsReply() { 1878 return (this.matches(DCCppConstants.MAXNUMSLOTS_REPLY_REGEX)); 1879 } 1880 1881 public boolean isDiagReply() { 1882 return (this.matches(DCCppConstants.DIAG_REPLY_REGEX)); 1883 } 1884 1885 public boolean isCurrentReply() { 1886 return (this.getOpCodeChar() == DCCppConstants.CURRENT_REPLY); 1887 } 1888 1889 public boolean isNamedCurrentReply() { 1890 return (this.matches(DCCppConstants.CURRENT_REPLY_NAMED_REGEX)); 1891 } 1892 1893 public boolean isMeterReply() { 1894 return (this.matches(DCCppConstants.METER_REPLY_REGEX)); 1895 } 1896 1897 public boolean isSensorReply() { 1898 return ((this.getOpCodeChar() == DCCppConstants.SENSOR_REPLY) || 1899 (this.getOpCodeChar() == DCCppConstants.SENSOR_REPLY_H) || 1900 (this.getOpCodeChar() == DCCppConstants.SENSOR_REPLY_L)); 1901 } 1902 1903 public boolean isSensorDefReply() { 1904 return (this.matches(DCCppConstants.SENSOR_DEF_REPLY_REGEX)); 1905 } 1906 1907 public boolean isTurnoutDefReply() { 1908 return (this.matches(DCCppConstants.TURNOUT_DEF_REPLY_REGEX)); 1909 } 1910 1911 public boolean isTurnoutDefDCCReply() { 1912 return (this.matches(DCCppConstants.TURNOUT_DEF_DCC_REPLY_REGEX)); 1913 } 1914 1915 public boolean isTurnoutDefServoReply() { 1916 return (this.matches(DCCppConstants.TURNOUT_DEF_SERVO_REPLY_REGEX)); 1917 } 1918 1919 public boolean isTurnoutDefVpinReply() { 1920 return (this.matches(DCCppConstants.TURNOUT_DEF_VPIN_REPLY_REGEX)); 1921 } 1922 1923 public boolean isTurnoutDefLCNReply() { 1924 return (this.matches(DCCppConstants.TURNOUT_DEF_LCN_REPLY_REGEX)); 1925 } 1926 1927 public boolean isMADCFailReply() { 1928 return (this.getOpCodeChar() == DCCppConstants.MADC_FAIL_REPLY); 1929 } 1930 1931 public boolean isMADCSuccessReply() { 1932 return (this.getOpCodeChar() == DCCppConstants.MADC_SUCCESS_REPLY); 1933 } 1934 1935 public boolean isStatusReply() { 1936 return (this.getOpCodeChar() == DCCppConstants.STATUS_REPLY); 1937 } 1938 1939 public boolean isOutputReply() { 1940 return (this.getOpCodeChar() == DCCppConstants.OUTPUT_REPLY); 1941 } 1942 1943 public boolean isOutputDefReply() { 1944 return (this.matches(DCCppConstants.OUTPUT_DEF_REPLY_REGEX)); 1945 } 1946 1947 public boolean isOutputCmdReply() { 1948 return (this.matches(DCCppConstants.OUTPUT_REPLY_REGEX)); 1949 } 1950 1951 public boolean isCommTypeReply() { 1952 return (this.matches(DCCppConstants.COMM_TYPE_REPLY_REGEX)); 1953 } 1954 1955 public boolean isWriteEepromReply() { 1956 return (this.matches(DCCppConstants.WRITE_EEPROM_REPLY_REGEX)); 1957 } 1958 1959 public boolean isLocoStateReply() { 1960 return (this.getOpCodeChar() == DCCppConstants.LOCO_STATE_REPLY); 1961 } 1962 1963 public boolean isTurnoutIDsReply() { 1964 return (this.matches(DCCppConstants.TURNOUT_IDS_REPLY_REGEX)); 1965 } 1966 public boolean isTurnoutIDReply() { 1967 return (this.matches(DCCppConstants.TURNOUT_ID_REPLY_REGEX)); 1968 } 1969 public boolean isRosterIDsReply() { 1970 return (this.matches(DCCppConstants.ROSTER_IDS_REPLY_REGEX)); 1971 } 1972 public boolean isRosterIDReply() { 1973 return (this.matches(DCCppConstants.ROSTER_ID_REPLY_REGEX)); 1974 } 1975 public boolean isAutomationIDsReply() { 1976 return (this.matches(DCCppConstants.AUTOMATION_IDS_REPLY_REGEX)); 1977 } 1978 public boolean isAutomationIDReply() { 1979 return (this.matches(DCCppConstants.AUTOMATION_ID_REPLY_REGEX)); 1980 } 1981 public boolean isCurrentMaxesReply() { 1982 return (this.matches(DCCppConstants.CURRENT_MAXES_REPLY_REGEX)); 1983 } 1984 public boolean isCurrentValuesReply() { 1985 return (this.matches(DCCppConstants.CURRENT_VALUES_REPLY_REGEX)); 1986 } 1987 public boolean isClockReply() { 1988 return (this.matches(DCCppConstants.CLOCK_REPLY_REGEX)); 1989 } 1990 1991 public boolean isTrackManagerReply() { 1992 return (this.matches(DCCppConstants.TRACKMANAGER_REPLY_REGEX)); 1993 } 1994 1995 public boolean isValidReplyFormat() { 1996 if ((this.matches(DCCppConstants.THROTTLE_REPLY_REGEX)) || 1997 (this.matches(DCCppConstants.MAXNUMSLOTS_REPLY_REGEX)) || 1998 (this.matches(DCCppConstants.TURNOUT_REPLY_REGEX)) || 1999 (this.matches(DCCppConstants.PROGRAM_REPLY_REGEX)) || 2000 (this.matches(DCCppConstants.PROGRAM_REPLY_V4_REGEX)) || 2001 (this.matches(DCCppConstants.PROGRAM_LOCOID_REPLY_REGEX)) || 2002 (this.matches(DCCppConstants.PROGRAM_VERIFY_REPLY_REGEX)) || 2003 (this.matches(DCCppConstants.TRACK_POWER_REPLY_REGEX)) || 2004 (this.matches(DCCppConstants.TRACK_POWER_REPLY_NAMED_REGEX)) || 2005 (this.matches(DCCppConstants.CURRENT_REPLY_REGEX)) || 2006 (this.matches(DCCppConstants.CURRENT_REPLY_NAMED_REGEX)) || 2007 (this.matches(DCCppConstants.METER_REPLY_REGEX)) || 2008 (this.matches(DCCppConstants.SENSOR_REPLY_REGEX)) || 2009 (this.matches(DCCppConstants.SENSOR_DEF_REPLY_REGEX)) || 2010 (this.matches(DCCppConstants.SENSOR_INACTIVE_REPLY_REGEX)) || 2011 (this.matches(DCCppConstants.SENSOR_ACTIVE_REPLY_REGEX)) || 2012 (this.matches(DCCppConstants.OUTPUT_REPLY_REGEX)) || 2013 (this.matches(DCCppConstants.OUTPUT_DEF_REPLY_REGEX)) || 2014 (this.matches(DCCppConstants.MADC_FAIL_REPLY_REGEX)) || 2015 (this.matches(DCCppConstants.MADC_SUCCESS_REPLY_REGEX)) || 2016 (this.matches(DCCppConstants.STATUS_REPLY_REGEX)) || 2017 (this.matches(DCCppConstants.STATUS_REPLY_BSC_REGEX)) || 2018 (this.matches(DCCppConstants.STATUS_REPLY_ESP32_REGEX)) || 2019 (this.matches(DCCppConstants.STATUS_REPLY_DCCEX_REGEX)) || 2020 (this.matches(DCCppConstants.LOCO_STATE_REGEX)) || 2021 (this.matches(DCCppConstants.TURNOUT_IDS_REPLY_REGEX)) || 2022 (this.matches(DCCppConstants.TURNOUT_ID_REPLY_REGEX)) || 2023 (this.matches(DCCppConstants.ROSTER_IDS_REPLY_REGEX)) || 2024 (this.matches(DCCppConstants.ROSTER_ID_REPLY_REGEX)) || 2025 (this.matches(DCCppConstants.AUTOMATION_IDS_REPLY_REGEX)) || 2026 (this.matches(DCCppConstants.AUTOMATION_ID_REPLY_REGEX)) || 2027 (this.matches(DCCppConstants.CURRENT_MAXES_REPLY_REGEX)) || 2028 (this.matches(DCCppConstants.CURRENT_VALUES_REPLY_REGEX)) || 2029 (this.matches(DCCppConstants.TURNOUT_IMPL_REGEX)) || 2030 (this.matches(DCCppConstants.TURNOUT_DEF_REPLY_REGEX)) || 2031 (this.matches(DCCppConstants.TURNOUT_DEF_DCC_REPLY_REGEX)) || 2032 (this.matches(DCCppConstants.TURNOUT_DEF_SERVO_REPLY_REGEX)) || 2033 (this.matches(DCCppConstants.TURNOUT_DEF_VPIN_REPLY_REGEX)) || 2034 (this.matches(DCCppConstants.TURNOUT_DEF_LCN_REPLY_REGEX)) || 2035 (this.matches(DCCppConstants.LCD_TEXT_REPLY_REGEX)) || 2036 (this.matches(DCCppConstants.CLOCK_REPLY_REGEX)) || 2037 (this.matches(DCCppConstants.DIAG_REPLY_REGEX)) || 2038 (this.matches(DCCppConstants.TRACKMANAGER_REPLY_REGEX))) { 2039 return (true); 2040 } else { 2041 return (false); 2042 } 2043 } 2044 2045 // initialize logging 2046 private final static Logger log = LoggerFactory.getLogger(DCCppReply.class); 2047 2048}