Exemplo n.º 1
0
        /// <summary>
        /// Gets <see cref="TrackingFrame"/> from the queue with the specified timestamp, in ticks.  If no frame exists for
        /// the specified timestamp, one will be created.
        /// </summary>
        /// <param name="ticks">Timestamp, in ticks, for which to get or create <see cref="TrackingFrame"/>.</param>
        /// <remarks>
        /// Ticks can be any point in time so long time requested is greater than time of last published frame; this queue is
        /// used in a real-time scenario with time moving forward.  If a frame is requested for an old timestamp, null will
        /// be returned. Note that frame returned will be "best-fit" for given timestamp based on <see cref="FramesPerSecond"/>.
        /// </remarks>
        /// <returns>An existing or new <see cref="TrackingFrame"/> from the queue for the specified timestamp.</returns>
        public TrackingFrame GetFrame(long ticks)
        {
            TrackingFrame frame     = null;
            bool          nodeAdded = false;
            long          destinationTicks;

            // Calculate destination ticks for this frame
            destinationTicks = m_roundToNearestTimestamp ? Ticks.RoundToSubsecondDistribution(ticks, m_framesPerSecond) : Ticks.AlignToSubsecondDistribution(ticks, m_framesPerSecond, m_timeResolution);

            // Make sure ticks are newer than latest published ticks...
            if (destinationTicks > Thread.VolatileRead(ref m_publishedTicks))
            {
                // See if requested frame is already available (can do this outside lock with concurrent dictionary)
                if (m_frameHash.TryGetValue(destinationTicks, out frame))
                {
                    return(frame);
                }

                // Didn't find frame for this timestamp so we need to add a new one to the queue
#if MONO
                lock (m_queueLock)
                {
#else
                bool locked = false;

                try
                {
                    m_queueLock.Enter(ref locked);
#endif
                    // Another thread may have gotten to this task already, so check for this contingency...
                    if (m_frameHash.TryGetValue(destinationTicks, out frame))
                    {
                        return(frame);
                    }

                    // TODO: Add a flag to add frames to publish even if no data arrives for them as an alternate mode
                    // TODO: of operation. In this mode pre-populate frame queue with at least one second of data from
                    // TODO: given start time (was thinking up next even second). This mode is useful for generically
                    // TODO: using concentrator to create a set of frame data from a data set with possible missing
                    // TODO: data (e.g., a historian) to create an evenly timestamped export

                    // Create a new frame for this timestamp
                    frame = new TrackingFrame(m_createNewFrame(destinationTicks), m_downsamplingMethod);

                    if (m_frameList.Count > 0)
                    {
                        // Insert frame into proper sorted position...
                        LinkedListNode <TrackingFrame> node = m_frameList.Last;

                        do
                        {
                            if (destinationTicks > node.Value.Timestamp)
                            {
                                m_frameList.AddAfter(node, frame);
                                nodeAdded = true;
                                break;
                            }

                            node = node.Previous;
                        }while (node != null);
                    }

                    if (!nodeAdded)
                    {
                        m_frameList.AddFirst(frame);
                        m_head = frame;
                    }

                    // Since we'll be requesting this frame over and over, we'll use
                    // a hash table for quick frame lookups by timestamp
                    m_frameHash[destinationTicks] = frame;
                }
#if !MONO
                finally
                {
                    if (locked)
                    {
                        m_queueLock.Exit();
                    }
                }
#endif
            }

            return(frame);
        }

        #region [ GetFrame Testing Algorithm ]

        // Copy this code into a console application and reference GSF.Core.dll to test.

        //using System;
        //using System.Collections.Generic;
        //using System.Linq;
        //using System.Text;
        //using GSF;

        //namespace FrameTimestampTest
        //{
        //    public class Program
        //    {
        //        private static long s_timeResolution;
        //        private static long s_framesPerSecond;
        //        private static double s_ticksPerFrame;
        //        private static double s_millisecondsPerFrame;

        //        public static void Main(string[] args)
        //        {
        //            s_framesPerSecond = 30;
        //            s_ticksPerFrame = Ticks.PerSecond / (double)s_framesPerSecond;
        //            s_timeResolution = Ticks.PerMillisecond;
        //            s_millisecondsPerFrame = 1000.0 / s_framesPerSecond;

        //            // Test BPA PDCstream style milliseconds
        //            DateTime currentTime = DateTime.Now;
        //            int[] bpaMilliseconds = new int[] { 000, 033, 066, 100, 133, 166, 200, 233, 266, 300, 333, 366, 400, 433, 466, 500, 533, 566, 599, 633, 666, 699, 733, 766, 800, 833, 866, 900, 933, 966 };

        //            s_timeResolution = Ticks.PerMillisecond * 33;
        //            Console.WriteLine("BPA millisecond sorting test, TimeResolution = {0}", s_timeResolution);

        //            for (int i = 0; i < bpaMilliseconds.Length; i++)
        //            {
        //                long longTicks = (new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, currentTime.Hour, currentTime.Minute, currentTime.Second, bpaMilliseconds[i])).Ticks;
        //                long destination = GetFrame(longTicks);
        //                Console.WriteLine(string.Format("{0:000} ms : {1:000} ms", bpaMilliseconds[i], (new DateTime(destination)).Millisecond));
        //            }

        //            s_timeResolution = Ticks.PerMillisecond;
        //            Ticks sourceTime = ((Ticks)DateTime.Now).BaselinedTimestamp(BaselineTimeInterval.Second);

        //            for (int i = 0; i < s_framesPerSecond; i++)
        //            {
        //                int milliseconds = (int)(s_millisecondsPerFrame * i);
        //                long longTicks = ((new DateTime(sourceTime)).AddMilliseconds((double)milliseconds)).Ticks;
        //                long destination = GetFrame(longTicks);
        //                Console.WriteLine(string.Format("{0} - {1:000} ms : {2} - {3:000} ms", longTicks, milliseconds, destination, (new DateTime(destination)).Millisecond));
        //            }
        //            Console.WriteLine();

        //            double ticks = Ticks.PerSecond;

        //            // Test truncated timestamps
        //            for (int i = 0; i < s_framesPerSecond; i++)
        //            {
        //                long longTicks = (long)(ticks / s_timeResolution) * s_timeResolution;
        //                Console.WriteLine(string.Format("{0} : {1}", longTicks, GetFrame(longTicks)));
        //                ticks += s_ticksPerFrame;
        //            }
        //            Console.WriteLine();

        //            ticks = 2 * Ticks.PerSecond;

        //            // Test rounded timestamps
        //            for (int i = 0; i < s_framesPerSecond; i++)
        //            {
        //                long longTicks = (long)Math.Round(ticks / s_timeResolution) * s_timeResolution;
        //                Console.WriteLine(string.Format("{0} : {1}", longTicks, GetFrame(longTicks)));
        //                ticks += s_ticksPerFrame;
        //            }
        //            Console.WriteLine();

        //            ticks = 3 * Ticks.PerSecond;

        //            // Test upper range limits
        //            for (int i = 0; i < s_framesPerSecond; i++)
        //            {
        //                ticks += s_ticksPerFrame;
        //                long longTicks = (long)(ticks / s_timeResolution) * s_timeResolution;
        //                longTicks -= s_timeResolution;
        //                Console.WriteLine(string.Format("{0} : {1}", longTicks, GetFrame(longTicks)));
        //            }
        //            Console.ReadLine();
        //        }

        //        public static long GetFrame(long ticks)
        //        {
        //            long baseTicks, ticksBeyondSecond, frameIndex, destinationTicks, nextDestinationTicks;

        //            // Baseline timestamp to the top of the second
        //            baseTicks = ticks - ticks % Ticks.PerSecond;

        //            // Remove the seconds from ticks
        //            ticksBeyondSecond = ticks - baseTicks;

        //            // Calculate a frame index between 0 and s_framesPerSecond-1, corresponding to ticks
        //            // rounded down to the nearest frame
        //            frameIndex = (long)(ticksBeyondSecond / s_ticksPerFrame);

        //            // Calculate the timestamp of the nearest frame rounded up
        //            nextDestinationTicks = (frameIndex + 1) * Ticks.PerSecond / s_framesPerSecond;

        //            // Determine whether the desired frame is the nearest
        //            // frame rounded down or the nearest frame rounded up
        //            if (s_timeResolution <= 1)
        //            {
        //                if (nextDestinationTicks <= ticksBeyondSecond)
        //                    destinationTicks = nextDestinationTicks;
        //                else
        //                    destinationTicks = frameIndex * Ticks.PerSecond / s_framesPerSecond;
        //            }
        //            else
        //            {
        //                // If, after translating nextDestinationTicks to the time resolution, it is less than
        //                // or equal to ticks, nextDestinationTicks corresponds to the desired frame
        //                if ((nextDestinationTicks / s_timeResolution) * s_timeResolution <= ticksBeyondSecond)
        //                    destinationTicks = nextDestinationTicks;
        //                else
        //                    destinationTicks = frameIndex * Ticks.PerSecond / s_framesPerSecond;
        //            }

        //            // Recover the seconds that were removed
        //            destinationTicks += baseTicks;

        //            return destinationTicks;
        //        }
        //    }
        //}

        #endregion

        #endregion
    }