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