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