import IHiResTimerCounter from './iHiResTimerCounter' import HiResTimerCounterBrowser from './hiResTimerCounterBrowser'; import { TimerEventInterval } from './hiResTimerEnums'; import TimeSpan from './timespan'; import { Signal } from '@pilotlab/signals'; /** * Description: * * Provides a high-resolution timer. * * The resolution of the timer varies by system. You can determine the resolution of a given * HiResTimer instance by calling one of the ResolutionXXX methods. * * To use HiResTimer create a new instance and call Start() - or pass true to the constructor. * Call Stop() to stop a running timer. Call Start() to restart a stopped timer. * Call Start(true) to restart and reset a stopped timer. Call one of the ElapsedXXX methods * on a running or stopped timer to get the elapsed time in the units you prefer. * Check the IsRunning property to determine if the timer is running. * * Usage: * *

 *     int sleepCount = 750;
 *     String formattedCount = sleepCount.toString();
 *
 *     HiResTimer timer = new HiResTimer();
 *
 *     print("Timer resolution: ");
 *     print(timer.resolution() + " seconds, ");
 *     print(timer.resolutionMilliseconds() + "milliseconds, ");
 *     print(timer.resolutionMicroseconds() + "microseconds.");
 *
 *     /// Start the timer then go to sleep.
 *     timer.start();
 *     print("Timer started: sleeping for " + formattedCount + " milliseconds.");
 *
 *     // Time will accumulate for this sleep because the timer is running.
 *     Thread.sleep(sleepCount);
 *
 *     /// Pause the timer
 *     timer.stop();
 *
 *     print("Timer paused: sleeping for " + formattedCount + " milliseconds.");
 *
 *     /// Time will not accumulate for this sleep because the timer is paused.
 *     Thread.sleep(sleepCount);
 *
 *     /// Restart the timer and go back to sleep
 *     timer.start();
 *     print("Timer restarted: sleeping for " + formattedCount + " milliseconds.");
 *
 *     /// Time will accumulate for this sleep because the timer is running.
 *     Thread.sleep(sleepCount);
 *
 *     /// Stop timing and output the results.
 *     timer.stop();
 *
 *     print("Timer stopped\n");
 *     print("Run Time: ");
 *     print(timer.Elapsed() + " seconds, ");
 *     print(timer.ElapsedMilliseconds() + "milliseconds, ");
 *     print(timer.ElapsedMicroseconds() + "microseconds.");
 *
 * 
*/ export class HiResTimer { /** * Initializes a new instance and starts the timer if passed true. * @param [counter] A platform-specific high resolution timer counter. * @param {boolean} [isStartTimer] Controls whether the timer is started after the HiResTimer is initialized. */ constructor(counter:IHiResTimerCounter, isStartTimer:boolean = false, eventInterval:TimerEventInterval = TimerEventInterval.NONE) { this._counter = counter; this._eventInterval = eventInterval; this._start = 0; this._adjustment = 0; this._elapsedAtStop = 0; this.intervalPassed = new Signal(false); this._isPaused= false; this._isRunning = false; this._initialize(isStartTimer); } static timerDefault:HiResTimer = new HiResTimer(new HiResTimerCounterBrowser().newCounter(), true); get counter():IHiResTimerCounter { return this._counter; } private _counter:IHiResTimerCounter; get eventInterval():TimerEventInterval { return this._eventInterval; } set eventInterval(value:TimerEventInterval) { this._eventInterval = value; } private _eventInterval:TimerEventInterval = TimerEventInterval.NONE; /** * Stores the start count or the elapsed ticks depending on context. */ private _start:number; /** * Stores the amount of time to adjust the results of the timer to account * for the time it takes to run the HiResTimer code. */ private _adjustment:number; /** * Stores the JavaScript interval used to check events. */ private _intervalReference:any; private _isPaused:boolean; private _elapsedAtStop:number; /** * Initializes the HiResTimer. Does all the heavy lifting for the public constructors. * @param {boolean} [isStartTimer] Controls whether the timer is started after the HiResTimer is initialized. */ private _initialize(isStartTimer:boolean):void { /// Time the timer code so we will know how much of an adjustment is needed. this._adjustment = 0; this.start(); this._stop(false); this._adjustment = this.elapsedTicks; } intervalPassed:Signal; /** * Start the timer. * @param {boolean} [isResetTimer] Controls whether the timer is reset before starting. * Pass false and any new elapsed time will be added to the existing elapsed time. * Pass true and any existing elapsed time is lost and only the new elapsed time is preserved. */ start():void { if (this._isPaused) this._start = this._counter.count - this._elapsedAtStop; // We are starting with an accumulated time else this._start = this._counter.count; // We are starting from 0 /// Handle events if necessary. if (this._eventInterval !== TimerEventInterval.NONE) { let interval = 1000; switch (this._eventInterval) { case TimerEventInterval.MILLISECONDS: interval = TimeSpan.unitsInMilliseconds.millisecond; break; case TimerEventInterval.CENTISECONDS: interval = TimeSpan.unitsInMilliseconds.centisecond; break; case TimerEventInterval.DECISECONDS: interval = TimeSpan.unitsInMilliseconds.decisecond; break; case TimerEventInterval.SECONDS: interval = TimeSpan.unitsInMilliseconds.second; break; case TimerEventInterval.MINUTES: case TimerEventInterval.HOURS: interval = TimeSpan.unitsInMilliseconds.minute; } this._intervalReference = setInterval(() => { this.intervalPassed.dispatch(new TimeSpan(this.elapsedMilliseconds)); }, interval); } this._isRunning = true; this._isPaused = false; } /** * Stop timing. Call one of the Elapsed____ methods to get the elapsed time since start() was * called. Call start(false) to restart the timer where you left off. Call start(true) to * restart the timer from 0. */ private _stop(isPause:boolean):void { /// Stop timing. Use start() afterwards to continue if (this._start <= 0) return; // The timer was not running this._elapsedAtStop = this.elapsedTicks; clearInterval(this._intervalReference); this._intervalReference = null; this._isPaused = isPause; this._isRunning = false; } pause():void { this._stop(true); } stop():void { this._stop(false); } /** * Indicates whether the timer is running or not. */ get isRunning():boolean { return this._isRunning; } private _isRunning:boolean; get elapsedTicks():number { let elapsedTicks:number = this._counter.count - this._start; if (elapsedTicks > this._adjustment) elapsedTicks -= this._adjustment; // Adjust for time timer code takes to run, but don't overflow else elapsedTicks = 0; // Stop must have been called directly after Start return elapsedTicks; } /** * Returns the number of microseconds elapsed since the timer started. * @return {number} The number of microseconds elapsed. */ get elapsedMicroseconds():number { return (this.elapsedTicks * 1000000) / this._counter.frequency; } /** * Returns the number of milliseconds elapsed since the timer started. * @return {number} The number of milliseconds elapsed. */ get elapsedMilliseconds():number { return (this.elapsedTicks * 1000) / this._counter.frequency; } /** * Returns the number of seconds elapsed since the timer started. * The number of seconds elapsed. */ get elapsedSeconds():number { return this.elapsedTicks / this._counter.frequency; } /** * Returns the number of minutes elapsed since the timer started. * The number of minutes elapsed. */ get elapsedMinutes():number { return Math.floor(TimeSpan.unitsInMilliseconds.minute / this.elapsedMilliseconds); } /** * Returns the number of hours elapsed since the timer started. * The number of hours elapsed. */ get elapsedHours():number { return Math.floor(TimeSpan.unitsInMilliseconds.hour / this.elapsedMilliseconds); } /** * A performance profiling utility property that returns the number of microseconds * that have passed since the last time the property was requested. */ get profileElapsed():number { let profileElapsedCurrent:number = Math.round(this.elapsedMicroseconds); let difference:number = profileElapsedCurrent - this._profileElapsedPrevious; this._profileElapsedPrevious = profileElapsedCurrent; return difference; } private _profileElapsedPrevious:number = 0; } // End class export default HiResTimer;