Source code ev3dev2/stopwatch.py

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
"""
A StopWatch class for tracking the amount of time between events
"""

from ev3dev2 import is_micropython

if is_micropython():
    import utime
else:
    import datetime as dt


def get_ticks_ms():
    if is_micropython():
        return utime.ticks_ms()
    else:
        return int(dt.datetime.timestamp(dt.datetime.now()) * 1000)


class StopWatchAlreadyStartedException(Exception):
    """
    Exception raised when start() is called on a StopWatch which was already start()ed and not yet
    stopped.
    """
    pass


class StopWatch(object):
    """
    A timer class which lets you start timing and then check the amount of time
    elapsed.
    """
    def __init__(self, desc=None):
        """
        Initializes the StopWatch but does not start it.

        desc:
            A string description to print when stringifying.
        """
        self.desc = desc
        self._start_time = None
        self._stopped_total_time = None

    def __str__(self):
        name = self.desc if self.desc is not None else self.__class__.__name__
        return "{}: {}".format(name, self.hms_str)

    def start(self):
        """
        Starts the timer. If the timer is already running, resets it.

        Raises a :py:class:`ev3dev2.stopwatch.StopWatchAlreadyStartedException` if already started.
        """
        if self.is_started:
            raise StopWatchAlreadyStartedException()

        self._stopped_total_time = None
        self._start_time = get_ticks_ms()

    def stop(self):
        """
        Stops the timer. The time value of this Stopwatch is paused and will not continue increasing.
        """
        if self._start_time is None:
            return

        self._stopped_total_time = get_ticks_ms() - self._start_time
        self._start_time = None

    def reset(self):
        """
        Resets the timer and leaves it stopped.
        """
        self._start_time = None
        self._stopped_total_time = None

    def restart(self):
        """
        Resets and then starts the timer.
        """
        self.reset()
        self.start()

    @property
    def is_started(self):
        """
        True if the StopWatch has been started but not stoped (i.e., it's currently running), false otherwise.
        """
        return self._start_time is not None

    @property
    def value_ms(self):
        """
        Returns the value of the stopwatch in milliseconds
        """
        if self._stopped_total_time is not None:
            return self._stopped_total_time

        return get_ticks_ms() - self._start_time if self._start_time is not None else 0

    @property
    def value_secs(self):
        """
        Returns the value of the stopwatch in seconds
        """
        return self.value_ms / 1000

    @property
    def value_hms(self):
        """
        Returns this StopWatch's elapsed time as a tuple
        ``(hours, minutes, seconds, milliseconds)``.
        """
        (hours, x) = divmod(int(self.value_ms), 3600000)
        (mins, x) = divmod(x, 60000)
        (secs, x) = divmod(x, 1000)
        return hours, mins, secs, x

    @property
    def hms_str(self):
        """
        Returns the stringified value of the stopwatch in HH:MM:SS.msec format
        """
        return '%02d:%02d:%02d.%03d' % self.value_hms

    def is_elapsed_ms(self, duration_ms):
        """
        Returns True if this timer has measured at least ``duration_ms``
        milliseconds.
        Otherwise, returns False. If ``duration_ms`` is None, returns False.
        """

        return duration_ms is not None and self.value_ms >= duration_ms

    def is_elapsed_secs(self, duration_secs):
        """
        Returns True if this timer has measured at least ``duration_secs`` seconds.
        Otherwise, returns False. If ``duration_secs`` is None, returns False.
        """

        return duration_secs is not None and self.value_secs >= duration_secs