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;