/// <summary> /// Create a new <see cref="PrecisionInputTimer"/> class. /// </summary> /// <param name="framesPerSecond">Desired frame rate for <see cref="PrecisionTimer"/>.</param> internal PrecisionInputTimer(int framesPerSecond) { // Create synchronization objects m_timerTickLock = new SpinLock(); m_frameWaitHandleA = new ManualResetEventSlim(false); m_frameWaitHandleB = new ManualResetEventSlim(false); m_useWaitHandleA = true; m_framesPerSecond = framesPerSecond; // Create a new precision timer for this timer state m_timer = new PrecisionTimer(); m_timer.Resolution = 1; m_timer.Period = 1; m_timer.AutoReset = true; // Attach handler for timer ticks m_timer.Tick += m_timer_Tick; m_frameWindowSize = (int)Math.Round(1000.0D / framesPerSecond) * 2; m_frameMilliseconds = new int[framesPerSecond]; for (int frameIndex = 0; frameIndex < framesPerSecond; frameIndex++) { m_frameMilliseconds[frameIndex] = (int)(1.0D / framesPerSecond * (frameIndex * 1000.0D)); } // Start high resolution timer on a separate thread so the start // time can synchronized to the top of the millisecond ThreadPool.QueueUserWorkItem(SynchronizeInputTimer); }
/// <summary> /// Releases the unmanaged resources used by the <see cref="FrameRateTimer"/> object and optionally releases the managed resources. /// </summary> /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> private void Dispose(bool disposing) { if (!m_disposed) { try { if (disposing) { if (m_timer != null) { if (m_processingInterval == -1) m_timer.Tick -= SetTimerPeriod; m_timer.Dispose(); } m_timer = null; } } finally { m_disposed = true; // Prevent duplicate dispose. } } }
/// <summary> /// Create a new <see cref="FrameRateTimer"/> class. /// </summary> /// <param name="framesPerSecond">Desired frame rate for <see cref="PrecisionTimer"/>.</param> /// <param name="processingInterval">Desired processing interval, if applicable.</param> /// <remarks> /// When the <paramref name="processingInterval"/> is set to -1, the frame rate timer interval will be calculated as a distribution /// of whole milliseonds over the specified number of <paramref name="framesPerSecond"/>. Otherwise the specified /// <paramref name="processingInterval"/> will be used as the timer interval. /// </remarks> public FrameRateTimer(int framesPerSecond, int processingInterval) { if (processingInterval == 0) throw new InvalidOperationException("A frame rate timer should not be created when using a processing interval of zero, i.e., processing data as fast as possible."); m_framesPerSecond = framesPerSecond; m_processingInterval = processingInterval; // Create a new precision timer for this timer state m_timer = new PrecisionTimer(); m_timer.AutoReset = true; if (processingInterval > 0) { // Establish fixed timer period m_timer.Period = processingInterval; } else { // Attach handler for timer period assignments m_timer.Tick += SetTimerPeriod; // Calculate distributed wait time periods over specified number of frames per second m_framePeriods = new int[framesPerSecond]; for (int frameIndex = 0; frameIndex < framesPerSecond; frameIndex++) { m_framePeriods[frameIndex] = CalcWaitTimeForFrameIndex(frameIndex); } // Establish initial timer period m_lastFramePeriod = m_framePeriods[0]; m_timer.Period = m_lastFramePeriod; } // Start timer m_timer.Start(); }
/// <summary> /// Releases the unmanaged resources used by the <see cref="ConcentratorBase"/> object and optionally releases the managed resources. /// </summary> /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (!m_disposed) { try { if (disposing) { if (m_publicationTimer != null) { m_publicationTimer.Tick -= PublishFrames; m_publicationTimer.Dispose(); } m_publicationTimer = null; if (m_frameQueue != null) { m_frameQueue.Dispose(); } m_frameQueue = null; if (m_monitorTimer != null) { m_monitorTimer.Elapsed -= MonitorUnpublishedSamples; m_monitorTimer.Dispose(); } m_monitorTimer = null; if (m_latestMeasurements != null) { m_latestMeasurements.Dispose(); } m_latestMeasurements = null; m_lastDiscardedMeasurement = null; } } finally { m_disposed = true; // Prevent duplicate dispose. } } }
private bool m_disposed; // Disposed flag detects redundant calls to dispose method #endregion #region [ Constructors ] /// <summary> /// Creates a new <see cref="ConcentratorBase"/>. /// </summary> /// <param name="framesPerSecond">Number of frames to publish per second.</param> /// <param name="lagTime">Past time deviation tolerance, in seconds - this becomes the amount of time to wait before publishing begins.</param> /// <param name="leadTime">Future time deviation tolerance, in seconds - this becomes the tolerated +/- accuracy of the local clock to real-time.</param> /// <remarks> /// <para> /// <paramref name="framesPerSecond"/> must be between 1 and 1000. /// </para> /// <para> /// <paramref name="lagTime"/> must be greater than zero, but can be specified in sub-second intervals (e.g., set to .25 for a quarter-second lag time). /// Note that this defines time sensitivity to past timestamps. /// </para> /// <para> /// <paramref name="leadTime"/> must be greater than zero, but can be specified in sub-second intervals (e.g., set to .5 for a half-second lead time). /// Note that this defines time sensitivity to future timestamps. /// </para> /// <para> /// Concentration will not begin until consumer "Starts" concentrator (i.e., calling <see cref="ConcentratorBase.Start"/> method or setting /// <c><see cref="ConcentratorBase.Enabled"/> = true</c>). /// </para> /// </remarks> /// <exception cref="ArgumentOutOfRangeException">Specified argument is outside of allowed value range (see remarks).</exception> protected ConcentratorBase(int framesPerSecond, double lagTime, double leadTime) { if (lagTime <= 0) throw new ArgumentOutOfRangeException("lagTime", "lagTime must be greater than zero, but it can be less than one"); if (leadTime <= 0) throw new ArgumentOutOfRangeException("leadTime", "leadTime must be greater than zero, but it can be less than one"); this.FramesPerSecond = framesPerSecond; #if UseHighResolutionTime m_realTimeTicks = PrecisionTimer.UtcNow.Ticks; #else m_realTime = DateTime.UtcNow.Ticks; #endif m_allowSortsByArrival = true; m_lagTime = lagTime; m_leadTime = leadTime; m_lagTicks = (long)(m_lagTime * Ticks.PerSecond); m_latestMeasurements = new ImmediateMeasurements(this); // Create a new queue for managing real-time frames m_frameQueue = new FrameQueue(this); // Create high precision timer used for frame processing m_publicationTimer = new PrecisionTimer(); m_publicationTimer.AutoReset = true; m_publicationTimer.Tick += PublishFrames; // This timer monitors the total number of unpublished samples every second. This is a useful statistic // to monitor: if total number of unpublished samples exceed lag time, measurement concentration could // be falling behind. m_monitorTimer = new System.Timers.Timer(); m_monitorTimer.Interval = 1000; m_monitorTimer.AutoReset = true; m_monitorTimer.Elapsed += MonitorUnpublishedSamples; }
// Protected Methods /// <summary>Shuts down concentrator in an orderly fashion.</summary> protected virtual void Dispose(bool disposing) { if (!m_disposed) { if (disposing) { if (m_publicationTimer != null) { m_publicationTimer.Tick -= PublishFrames; m_publicationTimer.Dispose(); } m_publicationTimer = null; if (m_frameQueue != null) { m_frameQueue.Dispose(); } m_frameQueue = null; if (m_monitorTimer != null) { m_monitorTimer.Elapsed -= MonitorUnpublishedSamples; m_monitorTimer.Dispose(); } m_monitorTimer = null; if (m_latestMeasurements != null) { m_latestMeasurements.Dispose(); } m_latestMeasurements = null; } } m_disposed = true; }
/// <summary> /// Releases the unmanaged resources used by the <see cref="FrameRateTimer"/> object and optionally releases the managed resources. /// </summary> /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (!m_disposed) { try { if (disposing) { if (m_timer != null) { m_timer.Tick -= SetTimerPeriod; m_timer.Dispose(); } m_timer = null; } } finally { m_disposed = true; // Prevent duplicate dispose. } } }
/// <summary> /// Create a new <see cref="FrameRateTimer"/> class. /// </summary> /// <param name="framesPerSecond">Desired frame rate for <see cref="PrecisionTimer"/>.</param> public FrameRateTimer(int framesPerSecond) { // Create a new precision timer for this timer state m_timer = new PrecisionTimer(); m_timer.AutoReset = true; // Attach handler for timer period assignments m_timer.Tick += SetTimerPeriod; m_framesPerSecond = framesPerSecond; m_framePeriods = new int[framesPerSecond]; // Calculate new wait time periods for new number of frames per second for (int frameIndex = 0; frameIndex < framesPerSecond; frameIndex++) { m_framePeriods[frameIndex] = CalcWaitTimeForFrameIndex(frameIndex); } // Establish initial timer period m_lastFramePeriod = m_framePeriods[0]; m_timer.Period = m_lastFramePeriod; // Start timer m_timer.Start(); }
/// <summary> /// Releases the unmanaged resources used by the <see cref="PrecisionInputTimer"/> object and optionally releases the managed resources. /// </summary> /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> private void Dispose(bool disposing) { if (!m_disposed) { try { if (disposing) { if (m_timer != null) { m_timer.Tick -= m_timer_Tick; m_timer.Dispose(); } m_timer = null; if (m_frameWaitHandleA != null) { m_frameWaitHandleA.Set(); m_frameWaitHandleA.Dispose(); } m_frameWaitHandleA = null; if (m_frameWaitHandleB != null) { m_frameWaitHandleB.Set(); m_frameWaitHandleB.Dispose(); } m_frameWaitHandleB = null; } } finally { m_disposed = true; // Prevent duplicate dispose. } } }