307 lines · 10.9 KB · C#
RawDownload
//******************************************************************************************************//  FrameBasedRandomValueInputAdapter.cs - Gbtc////  Copyright © 2014, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://www.opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  04/26/2014 - Stephen C. Wills//       Generated original version of source code.////****************************************************************************************************** using System;using System.Collections.Generic;using System.ComponentModel;using GSF;using GSF.NumericalAnalysis;using GSF.Threading;using GSF.TimeSeries;using GSF.TimeSeries.Adapters; namespace TestingAdapters{    /// <summary>    /// Represents a class used to stream frames of measurements with random values meant to simulate a frame-based input source.    /// </summary>    [Description("Random Frames: Streams frames of measurements with random values meant to simulate a frame-based input source")]    public class FrameBasedRandomValueInputAdapter : InputAdapterBase    {        #region [ Members ]         // Constants         /// <summary>        /// Default value for the <see cref="PublishRate"/> property.        /// </summary>        public const double DefaultPublishRate = 30.0;        /// <summary>        /// The default number of milliseconds for the dealy;        /// </summary>        public const double DefaultLatency = 125.0;        /// <summary>        /// The default jitter in the channel (1 standard deviation);        /// </summary>        public const double DefaultJitter = 30;          // Fields        private double m_publishRate;        private GaussianDistribution m_latency;        private ScheduledTask m_timer;        private ScheduledTask m_statusUpdate;        private Random m_random;        private long m_nextPublicationTime;        private long m_nextPublicationTimeWithLatency;        private int m_unprocessedMeasurements;        private bool m_disposed;         #endregion         #region [ Properties ]         /// <summary>        /// Gets or sets the number of frames generated by the adapter per second.        /// </summary>        [ConnectionStringParameter,        DefaultValue(DefaultPublishRate),        Description("Defines the number of frames generated by the adapter per second.")]        public double PublishRate        {            get            {                return m_publishRate;            }            set            {                m_publishRate = value;            }        }         /// <summary>        /// Gets the flag indicating if this adapter supports temporal processing.        /// </summary>        public override bool SupportsTemporalProcessing        {            get            {                return false;            }        }         /// <summary>        /// Gets flag that determines if the data input connects asynchronously.        /// </summary>        /// <remarks>        /// Derived classes should return true when data input source is connects asynchronously, otherwise return false.        /// </remarks>        protected override bool UseAsyncConnect        {            get            {                return false;            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Initializes <see cref="MovingValueInputAdapter"/>.        /// </summary>        public override void Initialize()        {            m_random = new Random(Guid.NewGuid().GetHashCode());            Dictionary<string, string> settings;            string setting;             base.Initialize();            settings = Settings;             if (!settings.TryGetValue("publishRate", out setting) || !double.TryParse(setting, out m_publishRate))                m_publishRate = DefaultPublishRate;             if (m_publishRate <= 0.0D)                throw new InvalidOperationException(string.Format("publishRate({0}) must be greater than zero", m_publishRate));             double latency;            double jitter;            if (!settings.TryGetValue("latency", out setting) || !double.TryParse(setting, out latency))                latency = DefaultLatency;             if (!settings.TryGetValue("jitter", out setting) || !double.TryParse(setting, out jitter))                jitter = DefaultJitter;             //Clips at 3 time the latency and 1/3 latency            m_latency = new GaussianDistribution(latency, jitter, latency / 3, latency * 3);        }         /// <summary>        /// Gets a short one-line status of this <see cref="MovingValueInputAdapter"/>.        /// </summary>        /// <param name="maxLength">Maximum number of available characters for display.</param>        /// <returns>        /// A short one-line summary of the current status of this <see cref="MovingValueInputAdapter"/>.        /// </returns>        public override string GetShortStatus(int maxLength)        {            return string.Format("{0} random values generated so far...", ProcessedMeasurements).CenterText(maxLength);        }         /// <summary>        /// Attempts to connect to data input source.        /// </summary>        protected override void AttemptConnection()        {            m_nextPublicationTime = ToPublicationTime(DateTime.UtcNow.Ticks);            m_nextPublicationTimeWithLatency = m_nextPublicationTime + (long)(m_latency.Next() * TimeSpan.TicksPerMillisecond);             if ((object)m_timer == null)            {                m_timer = new ScheduledTask(ThreadingMode.ThreadPool);                m_timer.Running += m_timer_Running;            }            if ((object)m_statusUpdate == null)            {                m_statusUpdate = new ScheduledTask(ThreadingMode.ThreadPool);                m_statusUpdate.Running += m_statusUpdate_Running;            }            m_timer.Start();            m_statusUpdate.Start(10000);        }         void m_timer_Running(object sender, EventArgs<ScheduledTaskRunningReason> eventArgs)        {            if (eventArgs.Argument == ScheduledTaskRunningReason.Disposing)                return;            PublishFrames();        }         void m_statusUpdate_Running(object sender, EventArgs<ScheduledTaskRunningReason> eventArgs)        {            if (eventArgs.Argument == ScheduledTaskRunningReason.Disposing)                return;             if (!Enabled)                return;             if (m_unprocessedMeasurements > 10 * m_publishRate)            {                OnStatusMessage(string.Format("{0} unprocessed messages", m_unprocessedMeasurements));            }            m_statusUpdate.Start(10000);         }         /// <summary>        /// Attempts to disconnect from data input source.        /// </summary>        protected override void AttemptDisconnection()        {            if ((object)m_timer != null)            {                m_timer.Dispose();                m_timer = null;            }            if ((object)m_statusUpdate != null)            {                m_statusUpdate.Dispose();                m_statusUpdate = null;            }        }         /// <summary>        /// Releases the unmanaged resources used by the <see cref="MovingValueInputAdapter"/> 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 override void Dispose(bool disposing)        {            if (!m_disposed)            {                try                {                    if (disposing)                    {                        if ((object)m_timer != null)                        {                            m_timer.Dispose();                            m_timer = null;                        }                    }                }                finally                {                    m_disposed = true;          // Prevent duplicate dispose.                    base.Dispose(disposing);    // Call base class Dispose().                }            }        }         private void PublishFrames()        {            while (m_nextPublicationTimeWithLatency - DateTime.UtcNow.Ticks < 1 * TimeSpan.TicksPerMillisecond)            {                if (!Enabled)                    return;                 IMeasurement[] outputMeasurements = OutputMeasurements;                IMeasurement[] newMeasurements = new IMeasurement[outputMeasurements.Length];                for (int x = 0; x < outputMeasurements.Length; x++)                {                    newMeasurements[x] = Measurement.Clone(outputMeasurements[x], m_random.NextDouble(), m_nextPublicationTime);                }                OnNewMeasurements(newMeasurements);                 m_nextPublicationTime = GetNextPublicationTime(m_nextPublicationTime);                m_nextPublicationTimeWithLatency = m_nextPublicationTime + (long)(m_latency.Next() * TimeSpan.TicksPerMillisecond);                 m_unprocessedMeasurements = (int)((DateTime.UtcNow.Ticks - m_nextPublicationTime) / (Ticks.PerSecond / m_publishRate));             }             if (!Enabled)                return;             long delay = ((m_nextPublicationTimeWithLatency - DateTime.UtcNow.Ticks) / TimeSpan.TicksPerMillisecond);             if (delay < 1)                m_timer.Start();            else if (delay > 10000)                m_timer.Start(10000);            else                m_timer.Start((int)delay);         }         private long GetNextPublicationTime(long time)        {            double interval = Ticks.PerSecond / m_publishRate;            long nextTime = (long)Math.Round(time + interval);            return ToPublicationTime(nextTime);        }         private long ToPublicationTime(long time)        {            double interval = Ticks.PerSecond / m_publishRate;            long seconds = time / Ticks.PerSecond;            long subseconds = time % Ticks.PerSecond;            long index = (long)Math.Round(subseconds / interval);            return (seconds * Ticks.PerSecond) + (long)Math.Round(index * interval);        }         #endregion     }}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t