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, 30); 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, 3); 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(15000); 357 stepDuration = 1; 358 } else { 359 setSpeedMatchError(targetVStartSpeedKPH); 360 361 if (Math.abs(speedMatchError) < ALLOWED_SPEED_MATCH_ERROR) { 362 setThrottle(true, 28); 363 initNextSpeedMatcherState(SpeedMatcherState.RE_INIT_SPEED_TABLE); 364 } else { 365 vStart = getNextSpeedMatchValue(lastVStart, vStartMax, VSTART_MIN); 366 367 if (((lastVStart == vStartMax) || (lastVStart == VSTART_MIN)) && (vStart == lastVStart)) { 368 statusLabel.setText(Bundle.getMessage("StatSetSpeedFail", SpeedMatcherCV.VSTART.getName())); 369 logger.info("Unable to achieve desired speed for CV {}", SpeedMatcherCV.VSTART.getName()); 370 abort(); 371 break; 372 } 373 374 lastVStart = vStart; 375 writeVStart(vStart); 376 } 377 } 378 } 379 break; 380 381 case RE_INIT_SPEED_TABLE: 382 //Set Speed table steps 27 through lowestMaxSpeedStep to TOP_SPEED_STEP_MAX 383 //and the remaining steps through step 2 to 1 384 if (programmerState == ProgrammerState.IDLE) { 385 if (stepDuration == 0) { 386 initSpeedTableStepValue = TOP_SPEED_STEP_MAX; 387 initSpeedTableStep = SpeedTableStep.STEP27; 388 stepDuration = 1; 389 } 390 391 writeSpeedTableStep(initSpeedTableStep, initSpeedTableStepValue); 392 393 if (initSpeedTableStep.getSpeedStep() == speedMatchMaxSpeedStep) { 394 initSpeedTableStep = initSpeedTableStep.getPrevious(); 395 speedMatchSpeedTableStep = initSpeedTableStep; 396 initSpeedTableStepValue = INITIAL_STEP2; 397 } 398 else { 399 initSpeedTableStep = initSpeedTableStep.getPrevious(); 400 } 401 402 if (initSpeedTableStep.getSpeedStep() < 2) { 403 initNextSpeedMatcherState(SpeedMatcherState.FORWARD_SPEED_MATCH); 404 } 405 } 406 break; 407 408 case FORWARD_SPEED_MATCH: 409 //Use PID Controller to adjust table speed steps lowestMaxSpeedStep through 2 to the appropriate speed 410 if (programmerState == ProgrammerState.IDLE) { 411 speedMatchSpeedStepInner(lastSpeedTableStepCVValue, speedMatchSpeedTableStep.getSpeedStep(), SpeedMatcherState.POST_SPEED_MATCH); 412 } 413 break; 414 415 case POST_SPEED_MATCH: { 416 statusLabel.setText(Bundle.getMessage("StatRestoreThrottle")); 417 418 //un-brick Digitrax decoders 419 setThrottle(false, 0); 420 setThrottle(true, 0); 421 422 SpeedMatcherState nextState; 423 if (trimReverseSpeed) { 424 if (warmUpReverseSeconds > 0) { 425 nextState = SpeedMatcherState.REVERSE_WARM_UP; 426 } else { 427 nextState = SpeedMatcherState.REVERSE_SPEED_MATCH_TRIM; 428 } 429 } else { 430 nextState = SpeedMatcherState.COMPLETE; 431 } 432 initNextSpeedMatcherState(nextState); 433 break; 434 } 435 436 case REVERSE_WARM_UP: 437 //Run specified reverse warm up time at high speed in reverse 438 statusLabel.setText(Bundle.getMessage("StatReverseWarmUp", warmUpReverseSeconds - stepDuration)); 439 440 if (stepDuration >= warmUpReverseSeconds) { 441 initNextSpeedMatcherState(SpeedMatcherState.REVERSE_SPEED_MATCH_TRIM); 442 } else { 443 if (stepDuration == 0) { 444 setSpeedMatchStateTimerDuration(5000); 445 setThrottle(false, 28); 446 } 447 stepDuration += 5; 448 } 449 450 break; 451 452 case REVERSE_SPEED_MATCH_TRIM: 453 //Use PID controller logic to adjust reverse trim until high speed reverse speed matches forward 454 if (programmerState == ProgrammerState.IDLE) { 455 if (stepDuration == 0) { 456 statusLabel.setText(Bundle.getMessage("StatSettingReverseTrim")); 457 setThrottle(false, speedMatchMaxSpeedStep); 458 setSpeedMatchStateTimerDuration(8000); 459 stepDuration = 1; 460 } else { 461 setSpeedMatchError(speedMatchMaxSpeedKPH); 462 463 if (Math.abs(speedMatchError) < ALLOWED_SPEED_MATCH_ERROR) { 464 initNextSpeedMatcherState(SpeedMatcherState.COMPLETE); 465 } else { 466 reverseTrimValue = getNextSpeedMatchValue(lastReverseTrimValue, REVERSE_TRIM_MAX, REVERSE_TRIM_MIN); 467 468 if (((lastReverseTrimValue == REVERSE_TRIM_MAX) || (lastReverseTrimValue == REVERSE_TRIM_MIN)) && (reverseTrimValue == lastReverseTrimValue)) { 469 statusLabel.setText(Bundle.getMessage("StatSetReverseTrimFail")); 470 logger.info("Unable to trim reverse to match forward"); 471 abort(); 472 break; 473 } 474 475 lastReverseTrimValue = reverseTrimValue; 476 writeReverseTrim(reverseTrimValue); 477 } 478 } 479 } 480 break; 481 482 case COMPLETE: 483 if (programmerState == ProgrammerState.IDLE) { 484 statusLabel.setText(Bundle.getMessage("StatSpeedMatchComplete")); 485 setThrottle(true, 0); 486 initNextSpeedMatcherState(SpeedMatcherState.CLEAN_UP); 487 } 488 break; 489 490 case USER_STOPPED: 491 if (programmerState == ProgrammerState.IDLE) { 492 statusLabel.setText(Bundle.getMessage("StatUserStoppedSpeedMatch")); 493 setThrottle(true, 0); 494 initNextSpeedMatcherState(SpeedMatcherState.CLEAN_UP); 495 } 496 break; 497 498 case CLEAN_UP: 499 //wrap it up 500 if (programmerState == ProgrammerState.IDLE) { 501 cleanUpSpeedMatcher(); 502 } 503 break; 504 505 default: 506 cleanUpSpeedMatcher(); 507 logger.error("Unexpected speed match timeout"); 508 break; 509 } 510 511 if (speedMatcherState != SpeedMatcherState.IDLE) { 512 startSpeedMatchStateTimer(); 513 } 514 } 515 //</editor-fold> 516 517 //<editor-fold defaultstate="collapsed" desc="ThrottleListener Overrides"> 518 /** 519 * Called when a throttle is found 520 * 521 * @param t the requested DccThrottle 522 */ 523 @Override 524 public void notifyThrottleFound(DccThrottle t) { 525 super.notifyThrottleFound(t); 526 527 if (speedMatcherState == SpeedMatcherState.WAIT_FOR_THROTTLE) { 528 logger.info("Starting speed matching"); 529 // using speed matching timer to trigger each phase of speed matching 530 initNextSpeedMatcherState(SpeedMatcherState.INIT_THROTTLE); 531 startSpeedMatchStateTimer(); 532 } else { 533 cleanUpSpeedMatcher(); 534 } 535 } 536 //</editor-fold> 537 538 //<editor-fold defaultstate="collapsed" desc="Helper Functions"> 539 /** 540 * Helper function for speed matching the current speedMatchSpeedTableStep 541 * 542 * @param maxCVValue the maximum allowable value for the CV 543 * @param minCVValue the minimum allowable value for the CV 544 * @param nextState the SpeedMatcherState to advance to if speed matching 545 * is complete 546 */ 547 private void speedMatchSpeedStepInner(int maxCVValue, int minCVValue, SpeedMatcherState nextState) { 548 if (stepDuration == 0) { 549 speedStepTargetSpeedKPH = getSpeedStepScaleSpeedInKPH(speedMatchSpeedTableStep.getSpeedStep()); 550 551 statusLabel.setText(Bundle.getMessage("StatSettingSpeed", speedMatchSpeedTableStep.getCV() + " (Speed Step " + String.valueOf(speedMatchSpeedTableStep.getSpeedStep()) + ")")); 552 logger.info("Setting CV {} (speed step {}) to {} KPH ({} MPH)", speedMatchSpeedTableStep.getCV(), speedMatchSpeedTableStep.getSpeedStep(), String.valueOf(speedStepTargetSpeedKPH), String.valueOf(Speed.kphToMph(speedStepTargetSpeedKPH))); 553 554 setThrottle(true, speedMatchSpeedTableStep.getSpeedStep()); 555 556 writeSpeedTableStep(speedMatchSpeedTableStep, speedMatchCVValue); 557 558 setSpeedMatchStateTimerDuration(8000); 559 stepDuration = 1; 560 } else { 561 setSpeedMatchError(speedStepTargetSpeedKPH); 562 563 if (Math.abs(speedMatchError) < ALLOWED_SPEED_MATCH_ERROR) { 564 lastSpeedTableStepCVValue = speedMatchCVValue; 565 566 speedMatchSpeedTableStep = speedMatchSpeedTableStep.getPrevious(); 567 568 if (speedMatchSpeedTableStep != SpeedTableStep.STEP1) { 569 initNextSpeedMatcherState(speedMatcherState); 570 } else { 571 initNextSpeedMatcherState(nextState); 572 } 573 } else { 574 speedMatchCVValue = getNextSpeedMatchValue(lastSpeedMatchCVValue, maxCVValue, minCVValue); 575 576 if (((speedMatchCVValue == maxCVValue) || (speedMatchCVValue == minCVValue)) && (speedMatchCVValue == lastSpeedMatchCVValue)) { 577 statusLabel.setText(Bundle.getMessage("StatSetSpeedFail", speedMatchSpeedTableStep.getCV() + " (Speed Step " + String.valueOf(speedMatchSpeedTableStep.getSpeedStep()) + ")")); 578 logger.info("Unable to achieve desired speed for CV {} (Speed Step {})", speedMatchSpeedTableStep.getCV(), String.valueOf(speedMatchSpeedTableStep.getSpeedStep())); 579 abort(); 580 return; 581 } 582 583 lastSpeedMatchCVValue = speedMatchCVValue; 584 writeSpeedTableStep(speedMatchSpeedTableStep, speedMatchCVValue); 585 } 586 } 587 } 588 589 /** 590 * Aborts the speed matching process programmatically 591 */ 592 private void abort() { 593 initNextSpeedMatcherState(SpeedMatcherState.CLEAN_UP); 594 } 595 596 /** 597 * Stops the speed matching process due to user input 598 */ 599 private void userStop() { 600 initNextSpeedMatcherState(SpeedMatcherState.USER_STOPPED); 601 } 602 603 /** 604 * Sets up the speed match state by resetting the speed matcher with a value delta of 10, 605 * clearing the step duration, setting the timer duration, and setting the next state 606 * 607 * @param nextState next SpeedMatcherState to set 608 */ 609 protected void initNextSpeedMatcherState(SpeedMatcherState nextState) { 610 initNextSpeedMatcherState(nextState, 10); 611 } 612 613 /** 614 * Sets up the speed match state by resetting the speed matcher with the given value delta, 615 * clearing the step duration, setting the timer duration, and setting the next state 616 * 617 * @param nextState next SpeedMatcherState to set 618 * @param speedMatchValueDelta the value delta to use when resetting the speed matcher 619 */ 620 protected void initNextSpeedMatcherState(SpeedMatcherState nextState, int speedMatchValueDelta) { 621 resetSpeedMatcher(speedMatchValueDelta); 622 stepDuration = 0; 623 speedMatcherState = nextState; 624 setSpeedMatchStateTimerDuration(1800); 625 } 626 //</editor-fold> 627 628 //debugging logger 629 private final static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(SpeedStepScaleESUTableSpeedMatcher.class); 630}