简体   繁体   中英

Problematic if-Statement

I have tried asking this question two or three times before but have not been able to phase it properly so I want to try one more time. I am making a training stopwatch app. The function of the app is to count 10 seconds, to allow the trainee 10 seconds to prepare. Once that time has elapsed, it counts to 20 seconds in which the trainee does a hard workout for the 20 seconds. After that, there is a 10 second rest time. Then it loops back to the 20 second workout and continues this loop for 8 rounds.

My problem is that it will begin the 10 second preparation but then it loops back into the 10 second timer. For some reason my if else statement keeps looping back into the preparation time. I'm using a stopwatch and timespan for the the if statement.

private void timer_Tick(object sender, EventArgs e)
    {
        //throw new NotImplementedException();
        //timer timespan is more than zero, start stopwatch(get the prepare counter going)
        if (timerWatch.Elapsed < new TimeSpan(0, 0, 11))// if its 5sec
        {
            milllisecond = timerWatch.ElapsedMilliseconds;
            second = milllisecond / 1000;
            milllisecond = milllisecond % 1000;
            minute = second / 60;
            second = second % 60;
            hour = minute / 60;
            minute = minute % 60;
            txtblTime.Text = minute.ToString("00") + ":" + second.ToString("00");
        }
        else if (timerWatch.Elapsed >= new TimeSpan(0, 0, 10) && timerWatch.Elapsed < new TimeSpan(0, 0, 21))//more than 4sec
        {
            timerWatch.Restart();

            milllisecond = timerWatch.ElapsedMilliseconds;
            second = milllisecond / 1000;
            milllisecond = milllisecond % 1000;
            minute = second / 60;
            second = second % 60;
            hour = minute / 60;
            minute = minute % 60;
            txtblTime.Text = minute.ToString("00") + ":" + second.ToString("00");

            txtblPrepare.Visibility = System.Windows.Visibility.Collapsed;
            txtblGo.Visibility = System.Windows.Visibility.Visible;
        }
        else if (timerWatch.Elapsed < new TimeSpan(0, 0, 21))
        {
            timerWatch.Restart();

            milllisecond = timerWatch.ElapsedMilliseconds;
            second = milllisecond / 1000;
            milllisecond = milllisecond % 1000;
            minute = second / 60;
            second = second % 60;
            hour = minute / 60;
            minute = minute % 60;

            txtblTime.Text = minute.ToString("00") + ":" + second.ToString("00");
            txtblGo.Visibility = System.Windows.Visibility.Collapsed;
        }
        else
            txtblTime.Text = "Times Up!";                    
    }

What you are trying to implement is a so called finite-state machine . Inferring the state of this machine form the timer makes it hard to understand and read the code. I would store the state in a more direct way. As Plutonix says, use an enum

public enum TrainingState
{
    Stopped, // The state machine is not working yet or has finished its work.
    Preparing,
    Working,
    Resting
}

Additionally you will need a counter for the number of loops

private TrainingState _state = TrainingState.Stopped;
private int _roundNo;

Now you can formulate the state transitions like this

private void ChangeState()
{
    switch (_state) {
        case  TrainingState.Stopped:
            //TODO: Initialize and start the timer, display state
            _state = TrainingState.Preparing;
            break;
        case  TrainingState.Preparing:
            //TODO: Adjust timer intervall for working phase, display state
            _roundNo = 1; // We are going into the first round
           _state = TrainingState.Working;
            break;
        case  TrainingState.Working:
             //TODO: Adjust timer intervall for resting phase, display state
            _state = TrainingState.Resting;
            break;
        case  TrainingState.Resting:
            if (_roundNo == 8) {
                _state = TrainingState.Stopped;
                //TODO: stop timer, display state
            } else {
                //TODO: Adjust timer intervall for working phase, display state
                _roundNo++; // We are going into the next round
               _state = TrainingState.Working;
            }
            break;
    }
}

You will have to call this method when starting and inside timer_Tick .

private void timer_Tick(object sender, EventArgs e)
{
    ChangeState();
}

private void btnStart_Click(object sender, EventArgs e)
{
    ChangeState();
}

Here are the essentials for using an Enum to track the phase. This seems easier than tracking stopwatches and TimeSpans...when the timer goes off, things change.

private enum Phases
{
    Stopped,
    Prep,
    WorkOut,
    Rest
}

private Phases thisPhase;       // phase tracker
private int workOutCount;       // 8 cycle counter

private void Timer1_Tick(object sender, EventArgs e)
{
    Timer1.Enabled = false;

    // ToDo: Fiddle with controls as needed
    // also the durations...not sure if a new Prep
    // follows the final rest or if they are the same
    switch (thisPhase) {
        case Phases.Stopped:
            // start things off
            thisPhase = Phases.Prep;
            Timer1.Interval = 10000;
            // prep time
            break;

        case Phases.Prep:
            workOutCount = 1;
            thisPhase = Phases.WorkOut;
            Timer1.Interval = 20000;
            // work out time
            break;

        case Phases.WorkOut:
            thisPhase = Phases.Rest;
            Timer1.Interval = 10000;
            // rest time
            break;

        case Phases.Rest:
            workOutCount += 1;

            if (workOutCount == 8) {
                thisPhase = Phases.Prep;
                // perhaps to None, if there is an instruction timeout
                Timer1.Interval = 10000;
                // prep time
                // actually means 10sec rest + 10 prep before next workout task
            } else {
                // next workout starts in...
                Timer1.Interval = 10000;
                // prep time
            }

            break;
    }
    Timer1.Enabled = true;
}

Conversion from VB may be imperfect, but it should convey the idea.

You are using

timer.Restart()

which resets the timer, so it is again executing the first case. Check the MSDN documentation of Stopwatch . This answer, of-course assumes that you are using Stopwatch class for timer.

I think, it executes those cases atleast once so as to reset the timer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM