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