001package jmri.jmrix.bachrus.speedmatcher.basic; 002 003import jmri.DccThrottle; 004import jmri.jmrix.bachrus.Speed; 005 006/** 007 * This is a simple speed matcher which will speed match a locomotive to a given 008 * start and top speed using ESU's complex speed table. Speed steps 1, 10, 19, 009 * and 28 will be set according to values interpolated linearly between the 010 * given start and to speeds. Values for the remaining CVs will interpolated 011 * between these 4 CVs. This is done to reduce the time the speed match takes 012 * and to increase likelihood of success. 013 * 014 * @author Todd Wegter Copyright (C) 2024 015 */ 016public class BasicESUTableSpeedMatcher extends BasicSpeedMatcher { 017 018 //<editor-fold defaultstate="collapsed" desc="Constants"> 019 private final int INITIAL_VSTART = 1; 020 private final int INITIAL_VHIGH = 255; 021 private final int INITIAL_TRIM = 128; 022 private final int STEP28_VALUE = 255; 023 private final int STEP1_VALUE = 1; 024 025 private final int VHIGH_MAX = 255; 026 private final int VHIGH_MIN = INITIAL_VSTART + 1; 027 private final int STEP19_MIN = 19; 028 private final int STEP10_MIN = 10; 029 private final int VSTART_MIN = 1; 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 FORWARD_SPEED_MATCH_VHIGH, 047 FORWARD_SPEED_MATCH_VSTART, 048 FORWARD_SPEED_MATCH_STEP19, 049 RE_INIT_SPEED_TABLE_MIDDLE_THIRD, 050 FORWARD_SPEED_MATCH_STEP10, 051 INTERPOLATE_SPEED_TABLE, 052 POST_INTERPOLATE, 053 REVERSE_WARM_UP, 054 REVERSE_SPEED_MATCH_TRIM, 055 COMPLETE, 056 USER_STOPPED, 057 CLEAN_UP, 058 } 059 //</editor-fold> 060 061 //<editor-fold defaultstate="collapsed" desc="Instance Variables"> 062 private SpeedTableStep initSpeedTableStep; 063 private int initSpeedTableStepValue; 064 065 private SpeedTableStep interpolationSpeedTableStep; 066 067 private int speedMatchCVValue = INITIAL_VHIGH; 068 private int lastSpeedMatchCVValue = INITIAL_VHIGH; 069 070 private int reverseTrimValue = INITIAL_TRIM; 071 private int lastReverseTrimValue = INITIAL_TRIM; 072 073 private final float targetVHighSpeedKPH; 074 private final float targetStep19SpeedKPH; 075 private final float targetStep10SpeedKPH; 076 private final float targetVStartSpeedKPH; 077 078 private int vHigh = INITIAL_VHIGH; 079 private int lastVHigh = INITIAL_VHIGH; 080 private int step19CVValue; 081 private int step10CVValue; 082 private int vStart; 083 private int lastVStart = INITIAL_VSTART; 084 private int vStartMax; 085 086 private SpeedMatcherState speedMatcherState = SpeedMatcherState.IDLE; 087 //</editor-fold> 088 089 /** 090 * Constructs the BasicESUTableSpeedMatcher from a BasicSpeedMatcherConfig 091 * 092 * @param config BasicSpeedMatcherConfig 093 */ 094 public BasicESUTableSpeedMatcher(BasicSpeedMatcherConfig config) { 095 super(config); 096 097 targetVHighSpeedKPH = targetTopSpeedKPH; 098 targetStep19SpeedKPH = getSpeedForSpeedStep(SpeedTableStep.STEP19, targetStartSpeedKPH, targetTopSpeedKPH); 099 targetStep10SpeedKPH = getSpeedForSpeedStep(SpeedTableStep.STEP10, targetStartSpeedKPH, targetTopSpeedKPH); 100 targetVStartSpeedKPH = targetStartSpeedKPH; 101 } 102 103 //<editor-fold defaultstate="collapsed" desc="SpeedMatcher Overrides"> 104 /** 105 * Starts the speed matching process 106 * 107 * @return true if speed matching started successfully, false otherwise 108 */ 109 @Override 110 public boolean startSpeedMatcher() { 111 if (!validate()) { 112 return false; 113 } 114 115 //reset instance variables 116 speedMatchCVValue = INITIAL_VHIGH; 117 lastSpeedMatchCVValue = INITIAL_VHIGH; 118 reverseTrimValue = INITIAL_TRIM; 119 lastReverseTrimValue = INITIAL_TRIM; 120 121 speedMatcherState = SpeedMatcherState.WAIT_FOR_THROTTLE; 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 initSpeedTableStepValue = INITIAL_VSTART; 224 initSpeedTableStep = SpeedTableStep.STEP2; 225 stepDuration = 1; 226 } 227 228 if (initSpeedTableStep.getSpeedStep() > SpeedTableStep.STEP18.getSpeedStep()) { 229 initSpeedTableStepValue = INITIAL_VHIGH; 230 } 231 232 writeSpeedTableStep(initSpeedTableStep, initSpeedTableStepValue); 233 234 initSpeedTableStep = initSpeedTableStep.getNext(); 235 if (initSpeedTableStep == SpeedTableStep.STEP28) { 236 initNextSpeedMatcherState(SpeedMatcherState.INIT_FORWARD_TRIM); 237 } 238 } 239 break; 240 241 case INIT_FORWARD_TRIM: 242 //set forward trim to 128 (CV 66) 243 if (programmerState == ProgrammerState.IDLE) { 244 writeForwardTrim(INITIAL_TRIM); 245 initNextSpeedMatcherState(SpeedMatcherState.INIT_REVERSE_TRIM); 246 } 247 break; 248 249 case INIT_REVERSE_TRIM: 250 //set reverse trim to 128 (CV 95) 251 if (programmerState == ProgrammerState.IDLE) { 252 writeReverseTrim(INITIAL_TRIM); 253 initNextSpeedMatcherState(SpeedMatcherState.POST_INIT); 254 } 255 break; 256 257 case POST_INIT: { 258 statusLabel.setText(Bundle.getMessage("StatRestoreThrottle")); 259 260 //un-brick Digitrax decoders 261 setThrottle(false, 0); 262 setThrottle(true, 0); 263 264 SpeedMatcherState nextState; 265 if (warmUpForwardSeconds > 0) { 266 nextState = SpeedMatcherState.FORWARD_WARM_UP; 267 } else { 268 nextState = SpeedMatcherState.FORWARD_SPEED_MATCH_VHIGH; 269 } 270 initNextSpeedMatcherState(nextState, 30); 271 break; 272 } 273 274 case FORWARD_WARM_UP: 275 //Run 4 minutes at high speed forward 276 statusLabel.setText(Bundle.getMessage("StatForwardWarmUp", warmUpForwardSeconds - stepDuration)); 277 278 if (stepDuration >= warmUpForwardSeconds) { 279 initNextSpeedMatcherState(SpeedMatcherState.FORWARD_SPEED_MATCH_VHIGH, 30); 280 } else { 281 if (stepDuration == 0) { 282 setSpeedMatchStateTimerDuration(5000); 283 setThrottle(true, 28); 284 } 285 stepDuration += 5; 286 } 287 break; 288 289 case FORWARD_SPEED_MATCH_VHIGH: 290 //Use PID Controller to adjust vHigh (Speed Step 28) to the max speed 291 if (programmerState == ProgrammerState.IDLE) { 292 if (stepDuration == 0) { 293 statusLabel.setText(Bundle.getMessage("StatSettingSpeed", SpeedMatcherCV.VHIGH.getName())); 294 logger.info("Setting CV {} to {} KPH ({} MPH)", SpeedMatcherCV.VHIGH.getName(), String.valueOf(targetVHighSpeedKPH), String.valueOf(Speed.kphToMph(targetVHighSpeedKPH))); 295 setThrottle(true, 28); 296 setSpeedMatchStateTimerDuration(8000); 297 stepDuration = 1; 298 } else { 299 setSpeedMatchError(targetVHighSpeedKPH); 300 301 if (Math.abs(speedMatchError) < ALLOWED_SPEED_MATCH_ERROR) { 302 initNextSpeedMatcherState(SpeedMatcherState.FORWARD_SPEED_MATCH_VSTART, 3); 303 } else { 304 vHigh = getNextSpeedMatchValue(lastVHigh, VHIGH_MAX, VHIGH_MIN); 305 306 if (((lastVHigh == VHIGH_MAX) || (lastVHigh == VHIGH_MIN)) && (vHigh == lastVHigh)) { 307 statusLabel.setText(Bundle.getMessage("StatSetSpeedFail", SpeedMatcherCV.VHIGH.getName())); 308 logger.info("Unable to achieve desired speed for CV {}", SpeedMatcherCV.VHIGH.getName()); 309 abort(); 310 break; 311 } 312 313 lastVHigh = vHigh; 314 writeVHigh(vHigh); 315 } 316 } 317 } 318 break; 319 320 case FORWARD_SPEED_MATCH_VSTART: 321 //Use PID Controller to adjust vStart (Speed Step 1) to the min speed 322 if (programmerState == ProgrammerState.IDLE) { 323 if (stepDuration == 0) { 324 vStartMax = vHigh - 1; 325 statusLabel.setText(Bundle.getMessage("StatSettingSpeed", SpeedMatcherCV.VSTART.getName())); 326 logger.info("Setting CV {} to {} KPH ({} MPH)", SpeedMatcherCV.VSTART.getName(), String.valueOf(targetVStartSpeedKPH), String.valueOf(Speed.kphToMph(targetVStartSpeedKPH))); 327 setThrottle(true, 1); 328 setSpeedMatchStateTimerDuration(15000); 329 stepDuration = 1; 330 } else { 331 setSpeedMatchError(targetVStartSpeedKPH); 332 333 if (Math.abs(speedMatchError) < ALLOWED_SPEED_MATCH_ERROR) { 334 initNextSpeedMatcherState(SpeedMatcherState.FORWARD_SPEED_MATCH_STEP19); 335 } else { 336 vStart = getNextSpeedMatchValue(lastVStart, vStartMax, VSTART_MIN); 337 338 if (((lastVStart == vStartMax) || (lastVStart == VSTART_MIN)) && (vStart == lastVStart)) { 339 statusLabel.setText(Bundle.getMessage("StatSetSpeedFail", SpeedMatcherCV.VSTART.getName())); 340 logger.info("Unable to achieve desired speed for CV {}", SpeedMatcherCV.VSTART.getName()); 341 abort(); 342 break; 343 } 344 345 lastVStart = vStart; 346 writeVStart(vStart); 347 } 348 } 349 } 350 break; 351 352 case FORWARD_SPEED_MATCH_STEP19: 353 //Use PID Controller to adjust Speed Step 19 to the interpolated speed 354 if (programmerState == ProgrammerState.IDLE) { 355 if (stepDuration == 0) { 356 lastSpeedMatchCVValue = STEP28_VALUE; 357 } 358 speedMatchSpeedTableStep(SpeedTableStep.STEP19, targetStep19SpeedKPH, INITIAL_VHIGH, STEP19_MIN, SpeedMatcherState.RE_INIT_SPEED_TABLE_MIDDLE_THIRD); 359 step19CVValue = speedMatchCVValue; 360 } 361 break; 362 363 case RE_INIT_SPEED_TABLE_MIDDLE_THIRD: 364 //Re-initialize Speed Steps 10-18 based off value for Step 19 365 if (programmerState == ProgrammerState.IDLE) { 366 if (stepDuration == 0) { 367 initSpeedTableStep = SpeedTableStep.STEP18; 368 stepDuration = 1; 369 } 370 371 writeSpeedTableStep(initSpeedTableStep, step19CVValue); 372 373 if (initSpeedTableStep == SpeedTableStep.STEP10) { 374 initNextSpeedMatcherState(SpeedMatcherState.FORWARD_SPEED_MATCH_STEP10); 375 } else { 376 initSpeedTableStep = initSpeedTableStep.getPrevious(); 377 } 378 379 } 380 break; 381 382 case FORWARD_SPEED_MATCH_STEP10: 383 //Use PID Controller to adjust Speed Step 10 to the interpolated speed 384 if (programmerState == ProgrammerState.IDLE) { 385 if (stepDuration == 0) { 386 lastSpeedMatchCVValue = step19CVValue; 387 } 388 speedMatchSpeedTableStep(SpeedTableStep.STEP10, targetStep10SpeedKPH, step19CVValue - 9, STEP10_MIN, SpeedMatcherState.INTERPOLATE_SPEED_TABLE); 389 step10CVValue = speedMatchCVValue; 390 } 391 break; 392 393 case INTERPOLATE_SPEED_TABLE: { 394 //Interpolate the values of the intermediate speed steps 395 if (programmerState == ProgrammerState.IDLE) { 396 if (stepDuration == 0) { 397 setThrottle(true, 0); 398 interpolationSpeedTableStep = SpeedTableStep.STEP27; 399 stepDuration = 1; 400 } 401 402 int interpolatedSpeedStepCVValue = getInterpolatedSpeedTableCVValue(interpolationSpeedTableStep); 403 writeSpeedTableStep(interpolationSpeedTableStep, interpolatedSpeedStepCVValue); 404 405 do { 406 interpolationSpeedTableStep = interpolationSpeedTableStep.getPrevious(); 407 } while (interpolationSpeedTableStep == SpeedTableStep.STEP19 || interpolationSpeedTableStep == SpeedTableStep.STEP10); 408 409 if (interpolationSpeedTableStep == SpeedTableStep.STEP1) { 410 initNextSpeedMatcherState(SpeedMatcherState.POST_INTERPOLATE); 411 } 412 413 } 414 break; 415 } 416 417 case POST_INTERPOLATE: { 418 statusLabel.setText(Bundle.getMessage("StatRestoreThrottle")); 419 420 //un-brick Digitrax decoders 421 setThrottle(false, 0); 422 setThrottle(true, 0); 423 424 SpeedMatcherState nextState; 425 if (trimReverseSpeed) { 426 if (warmUpReverseSeconds > 0) { 427 nextState = SpeedMatcherState.REVERSE_WARM_UP; 428 } else { 429 nextState = SpeedMatcherState.REVERSE_SPEED_MATCH_TRIM; 430 } 431 } else { 432 nextState = SpeedMatcherState.COMPLETE; 433 } 434 initNextSpeedMatcherState(nextState); 435 break; 436 } 437 438 case REVERSE_WARM_UP: 439 //Run specified reverse warm up time at high speed in reverse 440 statusLabel.setText(Bundle.getMessage("StatReverseWarmUp", warmUpReverseSeconds - stepDuration)); 441 442 if (stepDuration >= warmUpReverseSeconds) { 443 initNextSpeedMatcherState(SpeedMatcherState.REVERSE_SPEED_MATCH_TRIM); 444 } else { 445 if (stepDuration == 0) { 446 setSpeedMatchStateTimerDuration(5000); 447 setThrottle(false, 28); 448 } 449 stepDuration += 5; 450 } 451 452 break; 453 454 case REVERSE_SPEED_MATCH_TRIM: 455 //Use PID controller logic to adjust reverse trim until high speed reverse speed matches forward 456 if (programmerState == ProgrammerState.IDLE) { 457 if (stepDuration == 0) { 458 statusLabel.setText(Bundle.getMessage("StatSettingReverseTrim")); 459 setThrottle(false, 28); 460 setSpeedMatchStateTimerDuration(8000); 461 stepDuration = 1; 462 } else { 463 setSpeedMatchError(targetTopSpeedKPH); 464 465 if (Math.abs(speedMatchError) < ALLOWED_SPEED_MATCH_ERROR) { 466 initNextSpeedMatcherState(SpeedMatcherState.COMPLETE); 467 } else { 468 reverseTrimValue = getNextSpeedMatchValue(lastReverseTrimValue, REVERSE_TRIM_MAX, REVERSE_TRIM_MIN); 469 470 if (((lastReverseTrimValue == REVERSE_TRIM_MAX) || (lastReverseTrimValue == REVERSE_TRIM_MIN)) && (reverseTrimValue == lastReverseTrimValue)) { 471 statusLabel.setText(Bundle.getMessage("StatSetReverseTrimFail")); 472 logger.info("Unable to trim reverse to match forward"); 473 abort(); 474 break; 475 } 476 477 lastReverseTrimValue = reverseTrimValue; 478 writeReverseTrim(reverseTrimValue); 479 } 480 } 481 } 482 break; 483 484 case COMPLETE: 485 if (programmerState == ProgrammerState.IDLE) { 486 statusLabel.setText(Bundle.getMessage("StatSpeedMatchComplete")); 487 setThrottle(true, 0); 488 initNextSpeedMatcherState(SpeedMatcherState.CLEAN_UP); 489 } 490 break; 491 492 case USER_STOPPED: 493 if (programmerState == ProgrammerState.IDLE) { 494 statusLabel.setText(Bundle.getMessage("StatUserStoppedSpeedMatch")); 495 setThrottle(true, 0); 496 initNextSpeedMatcherState(SpeedMatcherState.CLEAN_UP); 497 } 498 break; 499 500 case CLEAN_UP: 501 //wrap it up 502 if (programmerState == ProgrammerState.IDLE) { 503 cleanUpSpeedMatcher(); 504 } 505 break; 506 507 default: 508 cleanUpSpeedMatcher(); 509 logger.error("Unexpected speed match timeout"); 510 break; 511 } 512 513 if (speedMatcherState != SpeedMatcherState.IDLE) { 514 startSpeedMatchStateTimer(); 515 } 516 } 517 //</editor-fold> 518 519 //<editor-fold defaultstate="collapsed" desc="ThrottleListener Overrides"> 520 /** 521 * Called when a throttle is found 522 * 523 * @param t the requested DccThrottle 524 */ 525 @Override 526 public void notifyThrottleFound(DccThrottle t) { 527 super.notifyThrottleFound(t); 528 529 if (speedMatcherState == SpeedMatcherState.WAIT_FOR_THROTTLE) { 530 logger.info("Starting speed matching"); 531 // using speed matching timer to trigger each phase of speed matching 532 initNextSpeedMatcherState(SpeedMatcherState.INIT_THROTTLE); 533 startSpeedMatchStateTimer(); 534 } else { 535 cleanUpSpeedMatcher(); 536 } 537 } 538 //</editor-fold> 539 540 //<editor-fold defaultstate="collapsed" desc="Helper Functions"> 541 /** 542 * Gets the interpolated CV value for the given speed step in the speed 543 * table 544 * 545 * @param speedStep the SpeedTableStep to get the speed for 546 * @return the target speed for the given speed step in KPH 547 */ 548 private int getInterpolatedSpeedTableCVValue(SpeedTableStep speedStep) { 549 SpeedTableStep maxStep; 550 SpeedTableStep minStep; 551 int maxStepCVValue; 552 int minStepCVValue; 553 554 if (speedStep.getSpeedStep() >= SpeedTableStep.STEP19.getSpeedStep()) { 555 maxStep = SpeedTableStep.STEP28; 556 minStep = SpeedTableStep.STEP19; 557 maxStepCVValue = STEP28_VALUE; 558 minStepCVValue = step19CVValue; 559 } else if (speedStep.getSpeedStep() >= SpeedTableStep.STEP10.getSpeedStep()) { 560 maxStep = SpeedTableStep.STEP19; 561 minStep = SpeedTableStep.STEP10; 562 maxStepCVValue = step19CVValue; 563 minStepCVValue = step10CVValue; 564 } else { 565 maxStep = SpeedTableStep.STEP10; 566 minStep = SpeedTableStep.STEP1; 567 maxStepCVValue = step10CVValue; 568 minStepCVValue = STEP1_VALUE; 569 } 570 571 return Math.round(minStepCVValue + ((((float) (maxStepCVValue - minStepCVValue)) / (maxStep.getSpeedStep() - minStep.getSpeedStep())) * (speedStep.getSpeedStep() - minStep.getSpeedStep()))); 572 } 573 574 /** 575 * Helper function for speed matching a given speed step 576 * 577 * @param speedStep the SpeedTableStep to speed match 578 * @param targetSpeedKPH the target speed in KPH 579 * @param maxCVValue the maximum allowable value for the CV 580 * @param minCVValue the minimum allowable value for the CV 581 * @param nextState the SpeedMatcherState to advance to if speed 582 * matching is complete 583 */ 584 private void speedMatchSpeedTableStep(SpeedTableStep speedStep, float targetSpeedKPH, int maxCVValue, int minCVValue, SpeedMatcherState nextState) { 585 if (stepDuration == 0) { 586 statusLabel.setText(Bundle.getMessage("StatSettingSpeed", speedStep.getCV() + " (Speed Step " + String.valueOf(speedStep.getSpeedStep()) + ")")); 587 logger.info("Setting CV {} (speed step {}) to {} KPH ({} MPH)", speedStep.getCV(), speedStep.getSpeedStep(), String.valueOf(targetSpeedKPH), String.valueOf(Speed.kphToMph(targetSpeedKPH))); 588 setThrottle(true, speedStep.getSpeedStep()); 589 setSpeedMatchStateTimerDuration(8000); 590 stepDuration = 1; 591 } else { 592 setSpeedMatchError(targetSpeedKPH); 593 594 if (Math.abs(speedMatchError) < ALLOWED_SPEED_MATCH_ERROR) { 595 initNextSpeedMatcherState(nextState); 596 } else { 597 speedMatchCVValue = getNextSpeedMatchValue(lastSpeedMatchCVValue, maxCVValue, minCVValue); 598 599 if (((speedMatchCVValue == maxCVValue) || (speedMatchCVValue == minCVValue)) && (speedMatchCVValue == lastSpeedMatchCVValue)) { 600 statusLabel.setText(Bundle.getMessage("StatSetSpeedFail", speedStep.getCV() + " (Speed Step " + String.valueOf(speedStep.getSpeedStep()) + ")")); 601 logger.info("Unable to achieve desired speed for CV {} (Speed Step {})", speedStep.getCV(), String.valueOf(speedStep.getSpeedStep())); 602 abort(); 603 return; 604 } 605 606 lastSpeedMatchCVValue = speedMatchCVValue; 607 writeSpeedTableStep(speedStep, speedMatchCVValue); 608 } 609 } 610 } 611 612 /** 613 * Aborts the speed matching process programmatically 614 */ 615 private void abort() { 616 initNextSpeedMatcherState(SpeedMatcherState.CLEAN_UP); 617 } 618 619 /** 620 * Stops the speed matching process due to user input 621 */ 622 private void userStop() { 623 initNextSpeedMatcherState(SpeedMatcherState.USER_STOPPED); 624 } 625 626 /** 627 * Sets up the speed match state by resetting the speed matcher with a value delta of 10, 628 * clearing the step duration, setting the timer duration, and setting the next state 629 * 630 * @param nextState next SpeedMatcherState to set 631 */ 632 protected void initNextSpeedMatcherState(SpeedMatcherState nextState) { 633 initNextSpeedMatcherState(nextState, 10); 634 } 635 636 /** 637 * Sets up the speed match state by resetting the speed matcher with the given value delta, 638 * clearing the step duration, setting the timer duration, and setting the next state 639 * 640 * @param nextState next SpeedMatcherState to set 641 * @param speedMatchValueDelta the value delta to use when resetting the speed matcher 642 */ 643 protected void initNextSpeedMatcherState(SpeedMatcherState nextState, int speedMatchValueDelta) { 644 resetSpeedMatcher(speedMatchValueDelta); 645 stepDuration = 0; 646 speedMatcherState = nextState; 647 setSpeedMatchStateTimerDuration(1800); 648 } 649 //</editor-fold> 650 651 //debugging logger 652 private final static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(BasicESUTableSpeedMatcher.class); 653}