001package jmri.jmrix.bachrus.speedmatcher.speedStepScale; 002 003import java.util.Locale; 004 005import jmri.DccThrottle; 006import jmri.jmrix.bachrus.Speed; 007 008/** 009 * This is a speed step scale speed matcher which will speed match a locomotive 010 * such that its speed in mph/kph will be equal to its speed step in 128 speed 011 * step mode. This uses ESU's implementation of the complex speed table, and the 012 * locomotive's speed will plateau at either its actual top speed or the set max 013 * speed, whichever is lower. 014 * 015 * @author Todd Wegter Copyright (C) 2024 016 */ 017public class SpeedStepScaleESUTableSpeedMatcher extends SpeedStepScaleSpeedMatcher { 018 019 //<editor-fold defaultstate="collapsed" desc="Constants"> 020 private final int INITIAL_VSTART = 1; 021 private final int INITIAL_VHIGH = 255; 022 private final int INITIAL_STEP2 = 1; 023 private final int INITIAL_TRIM = 128; 024 025 private final int VHIGH_MAX = 255; 026 private final int VHIGH_MIN = INITIAL_VSTART + 1; 027 private final int VSTART_MIN = 1; 028 029 private final int TOP_SPEED_STEP_MAX = 255; 030 //</editor-fold> 031 032 //<editor-fold defaultstate="collapsed" desc="Enums"> 033 protected enum SpeedMatcherState { 034 IDLE, 035 WAIT_FOR_THROTTLE, 036 INIT_THROTTLE, 037 INIT_ACCEL, 038 INIT_DECEL, 039 INIT_VSTART, 040 INIT_VHIGH, 041 INIT_SPEED_TABLE, 042 INIT_FORWARD_TRIM, 043 INIT_REVERSE_TRIM, 044 POST_INIT, 045 FORWARD_WARM_UP, 046 READ_MAX_SPEED, 047 FORWARD_SPEED_MATCH_VHIGH, 048 FORWARD_SPEED_MATCH_VSTART, 049 RE_INIT_SPEED_TABLE, 050 FORWARD_SPEED_MATCH, 051 POST_SPEED_MATCH, 052 REVERSE_WARM_UP, 053 REVERSE_SPEED_MATCH_TRIM, 054 COMPLETE, 055 USER_STOPPED, 056 CLEAN_UP, 057 } 058 //</editor-fold> 059 060 //<editor-fold defaultstate="collapsed" desc="Instance Variables"> 061 private SpeedTableStep initSpeedTableStep; 062 private int initSpeedTableStepValue; 063 private SpeedTableStep speedMatchSpeedTableStep; 064 private int speedMatchMaxSpeedStep; 065 066 private float speedStepTargetSpeedKPH; 067 068 private int vHigh = INITIAL_VHIGH; 069 private int lastVHigh = INITIAL_VHIGH; 070 071 private int vStart = INITIAL_VSTART; 072 private int lastVStart = INITIAL_VSTART; 073 private int vStartMax; 074 private float targetVStartSpeedKPH; 075 076 private int speedMatchCVValue = TOP_SPEED_STEP_MAX; 077 private int lastSpeedMatchCVValue = TOP_SPEED_STEP_MAX; 078 private int lastSpeedTableStepCVValue = TOP_SPEED_STEP_MAX; 079 080 private int reverseTrimValue = INITIAL_TRIM; 081 private int lastReverseTrimValue = INITIAL_TRIM; 082 083 private SpeedMatcherState speedMatcherState = SpeedMatcherState.IDLE; 084 //</editor-fold> 085 086 /** 087 * Constructs the SpeedStepScaleESUTableSpeedMatcher from a 088 * SpeedStepScaleSpeedMatcherConfig 089 * 090 * @param config SpeedStepScaleSpeedMatcherConfig 091 */ 092 public SpeedStepScaleESUTableSpeedMatcher(SpeedStepScaleSpeedMatcherConfig config) { 093 super(config); 094 } 095 096 //<editor-fold defaultstate="collapsed" desc="SpeedMatcherOverrides"> 097 /** 098 * Starts the speed matching process 099 * 100 * @return true if speed matching started successfully, false otherwise 101 */ 102 @Override 103 public boolean startSpeedMatcher() { 104 if (!validate()) { 105 return false; 106 } 107 108 //reset instance variables 109 vHigh = INITIAL_VHIGH; 110 lastVHigh = INITIAL_VHIGH; 111 vStart = INITIAL_VSTART; 112 lastVStart = INITIAL_VSTART; 113 speedMatchCVValue = TOP_SPEED_STEP_MAX; 114 lastSpeedMatchCVValue = TOP_SPEED_STEP_MAX; 115 lastSpeedTableStepCVValue = TOP_SPEED_STEP_MAX; 116 reverseTrimValue = INITIAL_TRIM; 117 lastReverseTrimValue = INITIAL_TRIM; 118 measuredMaxSpeedKPH = 0; 119 speedMatchMaxSpeedKPH = 0; 120 121 speedMatcherState = SpeedMatcherState.WAIT_FOR_THROTTLE; 122 123 actualMaxSpeedField.setText("___"); 124 125 if (!initializeAndStartSpeedMatcher(e -> speedMatchTimeout())) { 126 cleanUpSpeedMatcher(); 127 return false; 128 } 129 130 startStopButton.setText(Bundle.getMessage("SpeedMatchStopBtn")); 131 132 return true; 133 } 134 135 /** 136 * Stops the speed matching process 137 */ 138 @Override 139 public void stopSpeedMatcher() { 140 if (!isSpeedMatcherIdle()) { 141 logger.info("Speed matching manually stopped"); 142 userStop(); 143 } else { 144 cleanUpSpeedMatcher(); 145 } 146 } 147 148 /** 149 * Indicates if the speed matcher is idle (not currently speed matching) 150 * 151 * @return true if idle, false otherwise 152 */ 153 @Override 154 public boolean isSpeedMatcherIdle() { 155 return speedMatcherState == SpeedMatcherState.IDLE; 156 } 157 158 /** 159 * Cleans up the speed matcher when speed matching is stopped or is finished 160 */ 161 @Override 162 protected void cleanUpSpeedMatcher() { 163 speedMatcherState = SpeedMatcherState.IDLE; 164 super.cleanUpSpeedMatcher(); 165 } 166 //</editor-fold> 167 168 //<editor-fold defaultstate="collapsed" desc="Speed Matcher State"> 169 /** 170 * Main speed matching timeout handler. This is the state machine that 171 * effectively does the speed matching process. 172 */ 173 private synchronized void speedMatchTimeout() { 174 switch (speedMatcherState) { 175 case WAIT_FOR_THROTTLE: 176 cleanUpSpeedMatcher(); 177 logger.error("Timeout waiting for throttle"); 178 statusLabel.setText(Bundle.getMessage("StatusTimeout")); 179 break; 180 181 case INIT_THROTTLE: 182 //set throttle to 0 for init 183 setThrottle(true, 0); 184 initNextSpeedMatcherState(SpeedMatcherState.INIT_ACCEL); 185 break; 186 187 case INIT_ACCEL: 188 //set acceleration momentum to 0 (CV 3) 189 if (programmerState == ProgrammerState.IDLE) { 190 writeMomentumAccel(INITIAL_MOMENTUM); 191 initNextSpeedMatcherState(SpeedMatcherState.INIT_DECEL); 192 } 193 break; 194 195 case INIT_DECEL: 196 //set deceleration mementum to 0 (CV 4) 197 if (programmerState == ProgrammerState.IDLE) { 198 writeMomentumDecel(INITIAL_MOMENTUM); 199 initNextSpeedMatcherState(SpeedMatcherState.INIT_VSTART); 200 } 201 break; 202 203 case INIT_VSTART: 204 //set vStart to 0 (CV 2) 205 if (programmerState == ProgrammerState.IDLE) { 206 writeVStart(INITIAL_VSTART); 207 initNextSpeedMatcherState(SpeedMatcherState.INIT_VHIGH); 208 } 209 break; 210 211 case INIT_VHIGH: 212 //set vHigh to 255 (CV 5) 213 if (programmerState == ProgrammerState.IDLE) { 214 writeVHigh(INITIAL_VHIGH); 215 initNextSpeedMatcherState(SpeedMatcherState.INIT_SPEED_TABLE); 216 } 217 break; 218 219 case INIT_SPEED_TABLE: 220 //initialize speed table steps 221 //don't need to set steps 1 or 28 since they are locked to 1 and 222 //255, respectively on ESU decoders 223 if (programmerState == ProgrammerState.IDLE) { 224 if (stepDuration == 0) { 225 initSpeedTableStep = SpeedTableStep.STEP2; 226 stepDuration = 1; 227 } 228 229 writeSpeedTableStep(initSpeedTableStep, getSpeedStepLinearValue(initSpeedTableStep.getSpeedStep())); 230 231 initSpeedTableStep = initSpeedTableStep.getNext(); 232 if (initSpeedTableStep == SpeedTableStep.STEP28) { 233 initNextSpeedMatcherState(SpeedMatcherState.INIT_FORWARD_TRIM); 234 } 235 } 236 break; 237 238 case INIT_FORWARD_TRIM: 239 //set forward trim to 128 (CV 66) 240 if (programmerState == ProgrammerState.IDLE) { 241 writeForwardTrim(INITIAL_TRIM); 242 initNextSpeedMatcherState(SpeedMatcherState.INIT_REVERSE_TRIM); 243 } 244 break; 245 246 case INIT_REVERSE_TRIM: 247 //set reverse trim to 128 (CV 95) 248 if (programmerState == ProgrammerState.IDLE) { 249 writeReverseTrim(INITIAL_TRIM); 250 initNextSpeedMatcherState(SpeedMatcherState.POST_INIT); 251 } 252 break; 253 254 case POST_INIT: { 255 statusLabel.setText(Bundle.getMessage("StatRestoreThrottle")); 256 257 //un-brick Digitrax decoders 258 setThrottle(false, 0); 259 setThrottle(true, 0); 260 261 SpeedMatcherState nextState; 262 if (warmUpForwardSeconds > 0) { 263 nextState = SpeedMatcherState.FORWARD_WARM_UP; 264 } else { 265 nextState = SpeedMatcherState.READ_MAX_SPEED; 266 } 267 initNextSpeedMatcherState(nextState); 268 break; 269 } 270 271 case FORWARD_WARM_UP: 272 //Run 4 minutes at high speed forward 273 statusLabel.setText(Bundle.getMessage("StatForwardWarmUp", warmUpForwardSeconds - stepDuration)); 274 275 if (stepDuration >= warmUpForwardSeconds) { 276 initNextSpeedMatcherState(SpeedMatcherState.READ_MAX_SPEED); 277 } else { 278 if (stepDuration == 0) { 279 setSpeedMatchStateTimerDuration(5000); 280 setThrottle(true, 28); 281 } 282 stepDuration += 5; 283 } 284 break; 285 286 case READ_MAX_SPEED: 287 //Run 10 second at high speed forward and record the speed 288 if (stepDuration == 0) { 289 statusLabel.setText("Recording locomotive's maximum speed"); 290 setSpeedMatchStateTimerDuration(10000); 291 setThrottle(true, 28); 292 stepDuration = 1; 293 } else { 294 measuredMaxSpeedKPH = currentSpeedKPH; 295 296 String statusMessage = String.format(Locale.getDefault(), 297 "Measured maximum speed = %.1f KPH (%.1f MPH)", 298 measuredMaxSpeedKPH, Speed.kphToMph(measuredMaxSpeedKPH)); 299 logger.info(statusMessage); 300 301 float speedMatchMaxSpeed; 302 303 if (measuredMaxSpeedKPH > targetMaxSpeedKPH) { 304 speedMatchMaxSpeedStep = targetMaxSpeedStep.getSpeedTableStep().getSpeedStep(); 305 speedMatchMaxSpeed = targetMaxSpeedStep.getSpeed(); 306 speedMatchMaxSpeedKPH = targetMaxSpeedKPH; 307 } else { 308 float measuredMaxSpeed = speedUnit == Speed.Unit.MPH ? Speed.kphToMph(measuredMaxSpeedKPH) : measuredMaxSpeedKPH; 309 speedMatchMaxSpeedStep = getNextLowestSpeedTableStepForSpeed(measuredMaxSpeed); 310 speedMatchMaxSpeed = getSpeedForSpeedTableStep(speedMatchMaxSpeedStep); 311 speedMatchMaxSpeedKPH = speedUnit == Speed.Unit.MPH ? Speed.mphToKph(speedMatchMaxSpeed): speedMatchMaxSpeed; 312 } 313 314 actualMaxSpeedField.setText(String.format(Locale.getDefault(), "%.1f", speedMatchMaxSpeed)); 315 316 initNextSpeedMatcherState(SpeedMatcherState.FORWARD_SPEED_MATCH_VHIGH, 30); 317 } 318 break; 319 320 case FORWARD_SPEED_MATCH_VHIGH: 321 //Use PID Controller to adjust vHigh (Speed Step 28) to the max speed 322 if (programmerState == ProgrammerState.IDLE) { 323 if (stepDuration == 0) { 324 statusLabel.setText(Bundle.getMessage("StatSettingSpeed", SpeedMatcherCV.VHIGH.getName())); 325 logger.info("Setting CV {} to {} KPH ({} MPH)", SpeedMatcherCV.VHIGH.getName(), String.valueOf(speedMatchMaxSpeedKPH), String.valueOf(Speed.kphToMph(speedMatchMaxSpeedKPH))); 326 setThrottle(true, 28); 327 setSpeedMatchStateTimerDuration(8000); 328 stepDuration = 1; 329 } else { 330 setSpeedMatchError(speedMatchMaxSpeedKPH); 331 332 if (Math.abs(speedMatchError) < ALLOWED_SPEED_MATCH_ERROR) { 333 initNextSpeedMatcherState(SpeedMatcherState.FORWARD_SPEED_MATCH_VSTART, 3); 334 } else { 335 vHigh = getNextSpeedMatchValue(lastVHigh, VHIGH_MAX, VHIGH_MIN); 336 337 if (((lastVHigh == VHIGH_MAX) || (lastVHigh == VHIGH_MIN)) && (vHigh == lastVHigh)) { 338 statusLabel.setText(Bundle.getMessage("StatSetSpeedFail", SpeedMatcherCV.VHIGH.getName())); 339 logger.info("Unable to achieve desired speed for CV {}", SpeedMatcherCV.VHIGH.getName()); 340 abort(); 341 break; 342 } 343 344 lastVHigh = vHigh; 345 writeVHigh(vHigh); 346 } 347 } 348 } 349 break; 350 351 case FORWARD_SPEED_MATCH_VSTART: 352 //Use PID Controller to adjust vStart (Speed Step 1) to the appropriate speed 353 if (programmerState == ProgrammerState.IDLE) { 354 if (stepDuration == 0) { 355 vStartMax = vHigh - 1; 356 targetVStartSpeedKPH = getSpeedStepScaleSpeedInKPH(SpeedTableStep.STEP1.getSpeedStep()); 357 statusLabel.setText(Bundle.getMessage("StatSettingSpeed", SpeedMatcherCV.VSTART.getName())); 358 logger.info("Setting CV {} to {} KPH ({} MPH)", SpeedMatcherCV.VSTART.getName(), String.valueOf(targetVStartSpeedKPH), String.valueOf(Speed.kphToMph(targetVStartSpeedKPH))); 359 setThrottle(true, 1); 360 setSpeedMatchStateTimerDuration(15000); 361 stepDuration = 1; 362 } else { 363 setSpeedMatchError(targetVStartSpeedKPH); 364 365 if (Math.abs(speedMatchError) < ALLOWED_SPEED_MATCH_ERROR) { 366 setThrottle(true, 28); 367 initNextSpeedMatcherState(SpeedMatcherState.RE_INIT_SPEED_TABLE); 368 } else { 369 vStart = getNextSpeedMatchValue(lastVStart, vStartMax, VSTART_MIN); 370 371 if (((lastVStart == vStartMax) || (lastVStart == VSTART_MIN)) && (vStart == lastVStart)) { 372 statusLabel.setText(Bundle.getMessage("StatSetSpeedFail", SpeedMatcherCV.VSTART.getName())); 373 logger.info("Unable to achieve desired speed for CV {}", SpeedMatcherCV.VSTART.getName()); 374 abort(); 375 break; 376 } 377 378 lastVStart = vStart; 379 writeVStart(vStart); 380 } 381 } 382 } 383 break; 384 385 case RE_INIT_SPEED_TABLE: 386 //Set Speed table steps 27 through lowestMaxSpeedStep to TOP_SPEED_STEP_MAX 387 //and the remaining steps through step 2 to 1 388 if (programmerState == ProgrammerState.IDLE) { 389 if (stepDuration == 0) { 390 initSpeedTableStepValue = TOP_SPEED_STEP_MAX; 391 initSpeedTableStep = SpeedTableStep.STEP27; 392 stepDuration = 1; 393 } 394 395 writeSpeedTableStep(initSpeedTableStep, initSpeedTableStepValue); 396 397 if (initSpeedTableStep.getSpeedStep() == speedMatchMaxSpeedStep) { 398 initSpeedTableStep = initSpeedTableStep.getPrevious(); 399 speedMatchSpeedTableStep = initSpeedTableStep; 400 initSpeedTableStepValue = INITIAL_STEP2; 401 } 402 else { 403 initSpeedTableStep = initSpeedTableStep.getPrevious(); 404 } 405 406 if (initSpeedTableStep.getSpeedStep() < 2) { 407 initNextSpeedMatcherState(SpeedMatcherState.FORWARD_SPEED_MATCH); 408 } 409 } 410 break; 411 412 case FORWARD_SPEED_MATCH: 413 //Use PID Controller to adjust table speed steps lowestMaxSpeedStep through 2 to the appropriate speed 414 if (programmerState == ProgrammerState.IDLE) { 415 speedMatchSpeedStepInner(lastSpeedTableStepCVValue, speedMatchSpeedTableStep.getSpeedStep(), SpeedMatcherState.POST_SPEED_MATCH); 416 } 417 break; 418 419 case POST_SPEED_MATCH: { 420 statusLabel.setText(Bundle.getMessage("StatRestoreThrottle")); 421 422 //un-brick Digitrax decoders 423 setThrottle(false, 0); 424 setThrottle(true, 0); 425 426 SpeedMatcherState nextState; 427 if (trimReverseSpeed) { 428 if (warmUpReverseSeconds > 0) { 429 nextState = SpeedMatcherState.REVERSE_WARM_UP; 430 } else { 431 nextState = SpeedMatcherState.REVERSE_SPEED_MATCH_TRIM; 432 } 433 } else { 434 nextState = SpeedMatcherState.COMPLETE; 435 } 436 initNextSpeedMatcherState(nextState); 437 break; 438 } 439 440 case REVERSE_WARM_UP: 441 //Run specified reverse warm up time at high speed in reverse 442 statusLabel.setText(Bundle.getMessage("StatReverseWarmUp", warmUpReverseSeconds - stepDuration)); 443 444 if (stepDuration >= warmUpReverseSeconds) { 445 initNextSpeedMatcherState(SpeedMatcherState.REVERSE_SPEED_MATCH_TRIM); 446 } else { 447 if (stepDuration == 0) { 448 setSpeedMatchStateTimerDuration(5000); 449 setThrottle(false, 28); 450 } 451 stepDuration += 5; 452 } 453 454 break; 455 456 case REVERSE_SPEED_MATCH_TRIM: 457 //Use PID controller logic to adjust reverse trim until high speed reverse speed matches forward 458 if (programmerState == ProgrammerState.IDLE) { 459 if (stepDuration == 0) { 460 statusLabel.setText(Bundle.getMessage("StatSettingReverseTrim")); 461 setThrottle(false, speedMatchMaxSpeedStep); 462 setSpeedMatchStateTimerDuration(8000); 463 stepDuration = 1; 464 } else { 465 setSpeedMatchError(speedMatchMaxSpeedKPH); 466 467 if (Math.abs(speedMatchError) < ALLOWED_SPEED_MATCH_ERROR) { 468 initNextSpeedMatcherState(SpeedMatcherState.COMPLETE); 469 } else { 470 reverseTrimValue = getNextSpeedMatchValue(lastReverseTrimValue, REVERSE_TRIM_MAX, REVERSE_TRIM_MIN); 471 472 if (((lastReverseTrimValue == REVERSE_TRIM_MAX) || (lastReverseTrimValue == REVERSE_TRIM_MIN)) && (reverseTrimValue == lastReverseTrimValue)) { 473 statusLabel.setText(Bundle.getMessage("StatSetReverseTrimFail")); 474 logger.info("Unable to trim reverse to match forward"); 475 abort(); 476 break; 477 } 478 479 lastReverseTrimValue = reverseTrimValue; 480 writeReverseTrim(reverseTrimValue); 481 } 482 } 483 } 484 break; 485 486 case COMPLETE: 487 if (programmerState == ProgrammerState.IDLE) { 488 statusLabel.setText(Bundle.getMessage("StatSpeedMatchComplete")); 489 setThrottle(true, 0); 490 initNextSpeedMatcherState(SpeedMatcherState.CLEAN_UP); 491 } 492 break; 493 494 case USER_STOPPED: 495 if (programmerState == ProgrammerState.IDLE) { 496 statusLabel.setText(Bundle.getMessage("StatUserStoppedSpeedMatch")); 497 setThrottle(true, 0); 498 initNextSpeedMatcherState(SpeedMatcherState.CLEAN_UP); 499 } 500 break; 501 502 case CLEAN_UP: 503 //wrap it up 504 if (programmerState == ProgrammerState.IDLE) { 505 cleanUpSpeedMatcher(); 506 } 507 break; 508 509 default: 510 cleanUpSpeedMatcher(); 511 logger.error("Unexpected speed match timeout"); 512 break; 513 } 514 515 if (speedMatcherState != SpeedMatcherState.IDLE) { 516 startSpeedMatchStateTimer(); 517 } 518 } 519 //</editor-fold> 520 521 //<editor-fold defaultstate="collapsed" desc="ThrottleListener Overrides"> 522 /** 523 * Called when a throttle is found 524 * 525 * @param t the requested DccThrottle 526 */ 527 @Override 528 public void notifyThrottleFound(DccThrottle t) { 529 super.notifyThrottleFound(t); 530 531 if (speedMatcherState == SpeedMatcherState.WAIT_FOR_THROTTLE) { 532 logger.info("Starting speed matching"); 533 // using speed matching timer to trigger each phase of speed matching 534 initNextSpeedMatcherState(SpeedMatcherState.INIT_THROTTLE); 535 startSpeedMatchStateTimer(); 536 } else { 537 cleanUpSpeedMatcher(); 538 } 539 } 540 //</editor-fold> 541 542 //<editor-fold defaultstate="collapsed" desc="Helper Functions"> 543 /** 544 * Helper function for speed matching the current speedMatchSpeedTableStep 545 * 546 * @param maxCVValue the maximum allowable value for the CV 547 * @param minCVValue the minimum allowable value for the CV 548 * @param nextState the SpeedMatcherState to advance to if speed matching 549 * is complete 550 */ 551 private void speedMatchSpeedStepInner(int maxCVValue, int minCVValue, SpeedMatcherState nextState) { 552 if (stepDuration == 0) { 553 speedStepTargetSpeedKPH = getSpeedStepScaleSpeedInKPH(speedMatchSpeedTableStep.getSpeedStep()); 554 555 statusLabel.setText(Bundle.getMessage("StatSettingSpeed", speedMatchSpeedTableStep.getCV() + " (Speed Step " + String.valueOf(speedMatchSpeedTableStep.getSpeedStep()) + ")")); 556 logger.info("Setting CV {} (speed step {}) to {} KPH ({} MPH)", speedMatchSpeedTableStep.getCV(), speedMatchSpeedTableStep.getSpeedStep(), String.valueOf(speedStepTargetSpeedKPH), String.valueOf(Speed.kphToMph(speedStepTargetSpeedKPH))); 557 558 setThrottle(true, speedMatchSpeedTableStep.getSpeedStep()); 559 560 writeSpeedTableStep(speedMatchSpeedTableStep, speedMatchCVValue); 561 562 setSpeedMatchStateTimerDuration(8000); 563 stepDuration = 1; 564 } else { 565 setSpeedMatchError(speedStepTargetSpeedKPH); 566 567 if (Math.abs(speedMatchError) < ALLOWED_SPEED_MATCH_ERROR) { 568 lastSpeedTableStepCVValue = speedMatchCVValue; 569 570 speedMatchSpeedTableStep = speedMatchSpeedTableStep.getPrevious(); 571 572 if (speedMatchSpeedTableStep != SpeedTableStep.STEP1) { 573 initNextSpeedMatcherState(speedMatcherState); 574 } else { 575 initNextSpeedMatcherState(nextState); 576 } 577 } else { 578 speedMatchCVValue = getNextSpeedMatchValue(lastSpeedMatchCVValue, maxCVValue, minCVValue); 579 580 if (((speedMatchCVValue == maxCVValue) || (speedMatchCVValue == minCVValue)) && (speedMatchCVValue == lastSpeedMatchCVValue)) { 581 statusLabel.setText(Bundle.getMessage("StatSetSpeedFail", speedMatchSpeedTableStep.getCV() + " (Speed Step " + String.valueOf(speedMatchSpeedTableStep.getSpeedStep()) + ")")); 582 logger.info("Unable to achieve desired speed for CV {} (Speed Step {})", speedMatchSpeedTableStep.getCV(), String.valueOf(speedMatchSpeedTableStep.getSpeedStep())); 583 abort(); 584 return; 585 } 586 587 lastSpeedMatchCVValue = speedMatchCVValue; 588 writeSpeedTableStep(speedMatchSpeedTableStep, speedMatchCVValue); 589 } 590 } 591 } 592 593 /** 594 * Aborts the speed matching process programmatically 595 */ 596 private void abort() { 597 initNextSpeedMatcherState(SpeedMatcherState.CLEAN_UP); 598 } 599 600 /** 601 * Stops the speed matching process due to user input 602 */ 603 private void userStop() { 604 initNextSpeedMatcherState(SpeedMatcherState.USER_STOPPED); 605 } 606 607 /** 608 * Sets up the speed match state by resetting the speed matcher with a value delta of 10, 609 * clearing the step duration, setting the timer duration, and setting the next state 610 * 611 * @param nextState next SpeedMatcherState to set 612 */ 613 protected void initNextSpeedMatcherState(SpeedMatcherState nextState) { 614 initNextSpeedMatcherState(nextState, 10); 615 } 616 617 /** 618 * Sets up the speed match state by resetting the speed matcher with the given value delta, 619 * clearing the step duration, setting the timer duration, and setting the next state 620 * 621 * @param nextState next SpeedMatcherState to set 622 * @param speedMatchValueDelta the value delta to use when resetting the speed matcher 623 */ 624 protected void initNextSpeedMatcherState(SpeedMatcherState nextState, int speedMatchValueDelta) { 625 resetSpeedMatcher(speedMatchValueDelta); 626 stepDuration = 0; 627 speedMatcherState = nextState; 628 setSpeedMatchStateTimerDuration(1800); 629 } 630 //</editor-fold> 631 632 //debugging logger 633 private final static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(SpeedStepScaleESUTableSpeedMatcher.class); 634}