Click here to Skip to main content
15,897,718 members
Articles / Web Development / ASP.NET

Legion: Build your own virtual super computer with Silverlight

Rate me:
Please Sign up or sign in to vote.
4.87/5 (139 votes)
27 Oct 2008LGPL321 min read 423.7K   1.1K   335  
Legion is a grid computing framework that uses the Silverlight CLR to execute user definable tasks. It provides grid-wide thread-safe operations for web clients. Client performance metrics, such as bandwidth and processor speed, may be used to tailor jobs. Also includes a WPF Manager application.
//  This code file for the WPF Realtime Line Graph control was developed
//  by Andre de Cavaignac and Daniel Simon at Lab49.
//
//  The code in this file can be freely used and redistributed in applications
//  providing that this file header is maintained in files relating to the
//  line graph control.
//
//  2007, Andre de Cavaignac and Daniel Simon
//
//  Lab49 Blog:
//      http://blog.lab49.com
//  Andre de Cavaignac's Blog:
//      http://decav.com
//  Andre de Cavaignac's Blog Article on this Control:
//      http://decav.com/blogs/andre/archive/2007/08/25/live-updating-line-graph-in-wpf.aspx
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Data;
using System.Diagnostics;
using System.Windows.Threading;

namespace Decav.Windows.Controls.LineGraph
{
    /// <summary>
    /// Manages a collection of <see cref="MiniTicker"/> and their connection to a datasource, sending the
    /// tickers <see cref="GraphTick"/> updates.
    /// </summary>
    public abstract class TickerAdapter : FrameworkElement
    {
        /// <summary>
        /// Initializes a new <see cref="TickerAdapter"/> instance.
        /// </summary>
        public TickerAdapter()
        {
            _securitiesReadOnly = new ReadOnlyCollection<Security>(_securities);
            GraphDuration = TimeSpan.FromDays(1);
        }

        
        private Stopwatch _timeSinceStart = new Stopwatch();
        private Collection<Security> _securities = new Collection<Security>();
        private Dictionary<string, Security> _securitiesLookup = new Dictionary<string, Security>(StringComparer.InvariantCultureIgnoreCase);
        private ReadOnlyCollection<Security> _securitiesReadOnly;

        private readonly ObservableCollection<TickerInfo> _tickers = new ObservableCollection<TickerInfo>();

        /// <summary>
        /// The lookup with all the tickers keyed by their security ID.
        /// </summary>
        private readonly Dictionary<string, TickerInfo> _tickersLookup = new Dictionary<string, TickerInfo>(StringComparer.InvariantCultureIgnoreCase);

        /// <summary>
        /// Gets the amound of time that has elapsed since the <see cref="TickerAdapter.Start"/> method
        /// was called.
        /// </summary>
        /// <remarks>
        /// Used to stamp the recieved date on a <see cref="GraphTick"/>.
        /// </remarks>
        protected TimeSpan ElapsedSinceStart
        {
            get
            {
                return _timeSinceStart.Elapsed;
            }
        }
        
        /// <summary>
        /// Gets a collection of the tickers that have been created for the incoming data.
        /// </summary>
        public ObservableCollection<TickerInfo> Tickers 
        {
            get
            {
                return _tickers;
            }
        }

        /// <summary>
        /// Gets a collection of the securities exposed by the adapter.
        /// </summary>
        public ReadOnlyCollection<Security> Securities
        {
            get
            {
                return _securitiesReadOnly;
            }
        }
        
        #region GraphDuration
        /// <summary>
        /// The dependency property that gets or sets the window of time that a graph displays between its leftmost and rightmost edges.
        /// </summary>
        public static DependencyProperty GraphDurationProperty = DependencyProperty.Register(
            "GraphDuration", typeof(TimeSpan), typeof(TickerAdapter));

        /// <summary>
        /// Gets or sets the window of time that a graph displays between its leftmost and rightmost edges.
        /// </summary>
        public TimeSpan GraphDuration
        {
            get { return (TimeSpan)GetValue(GraphDurationProperty); }
            set { SetValue(GraphDurationProperty, value); }
        }
        #endregion

        /// <summary>
        /// Checks if a ticker exists for a security.
        /// </summary>
        /// <param name="id">The ID of the security to check for.</param>
        /// <returns>True if the ticker exists for the security, else false.</returns>
        public bool ContainsTicker(string id)
        {
            if (id == null)
                throw new ArgumentNullException("id");
        

            return _tickersLookup.ContainsKey(id);
        }

        /// <summary>
        /// Creates a new ticker that the adapter will populate.
        /// </summary>
        /// <param name="security">The security to create the ticker for.</param>
        /// <returns>The ticker information for the new ticker.</returns>
        public TickerInfo CreateTicker(Security security)
        {
            if (security == null)
                throw new ArgumentNullException("security");

            lock (_tickers)
            {
                if (_tickersLookup.ContainsKey(security.Name))
                    throw new ArgumentException("A ticker for the specified security already exists.");

                MiniTicker ticker = null;
                Dispatcher.Invoke(DispatcherPriority.Normal, (MethodInvoker)delegate()
                {
                    ticker = new MiniTicker();
                    ticker.Title = security.Name;

                    ticker.SetBinding(MiniTicker.DurationProperty,
                        new Binding("GraphDuration") { Source = this, Mode = BindingMode.OneWay });

                    ticker.Start();
                });

                TickerInfo info = new TickerInfo(security, ticker);
                _tickersLookup.Add(security.Id, info);
                _tickers.Add(info);
                OnTickerAdded(new TickerAddedEventArgs(ticker));

                return info;
            }
        }

        /// <summary>
        /// Adds a security to the list of exposed securities.
        /// </summary>
        /// <param name="security">The security to add.</param>
        protected void AddSecurity(Security security)
        {
            if (security == null)
                throw new ArgumentNullException("security");

            _securities.Add(security);
            _securitiesLookup.Add(security.Id, security);
            OnSecurityAdded(new SecurityAddedEventArgs(security));
        }

        /// <summary>
        /// Checks if the security is within the <see cref="Securities"/> collection.
        /// </summary>
        /// <param name="id">The ID of the security to find.</param>
        /// <returns>True if it exists, else false.</returns>
        protected bool ContainsSecurity(string id)
        {
            if (id == null)
                throw new ArgumentNullException("id");

            return _securitiesLookup.ContainsKey(id);
        }

        /// <summary>
        /// Starts the ticker adapter receiving data and creating <see cref="MiniTicker"/> items
        /// in the <see cref="Tickers"/> collection
        /// </summary>
        public void Start()
        {
            OnStarted(EventArgs.Empty);
            _timeSinceStart.Start();
        }

        /// <summary>
        /// Notifies the base class that a new <see cref="GraphTick"/>
        /// has been received.
        /// </summary>
        /// <param name="securityId">The uniqiue identifier of the security that the tick is for.</param>
        /// <param name="tick">The tick that was received.</param>
        protected void NotifyNewTick(string securityId, GraphTick tick)
        {
            if (securityId == null)
                throw new ArgumentNullException("securityId");
        
            TimeSpan currentTickTime = _timeSinceStart.Elapsed;

            // Get the ticker this is relevent to and add the tick to that ticker.
            if (!ContainsTicker(securityId))
                return;

            // Only add to the Security (the model), the UI updates will be flushed
            // by a timer.  This is more performant because we queue less delegates
            // onto the dispatcher....
            TickerInfo ticker = GetTicker(securityId);

            Dispatcher.Invoke(DispatcherPriority.Normal, (MethodInvoker)delegate()
            {
                GraphTickCollection ticks = ticker.Ticker.Ticks;
                if (tick.Time < ticks.MaximumTime)
                {
                    Console.WriteLine("ERROR: Tick is before the last tick.  Ticks out of order... cleaning up...");

                    // Cleanup any ticks that are out of order.
                    for (int i = ticks.Count - 1; i >= 0; i--)
                    {
                        GraphTick curTick = ticks[i];

                        if (curTick.Time > tick.Time)
                            ticks.RemoveAt(i);
                    }
                }

                ticks.Add(tick);
            });
        }

        /// <summary>
        /// Gets or creates a ticker based on a security name.
        /// </summary>
        /// <param name="id">The unique identifier of the ticker.</param>
        /// <param name="name">The display name of the security to get or create a ticker for.</param>
        /// <returns>The ticker for the specified security.</returns>
        public TickerInfo GetTicker(string id)
        {
            if (id == null)
                throw new ArgumentNullException("id");

            lock (_tickers)
            {
                TickerInfo info = null;
                if (_tickersLookup.TryGetValue(id, out info))
                    return info;
                else
                    throw new InvalidOperationException("Cannot get a ticker that does not exist.");
            }
        }

        #region Started Event
        /// <summary>
        /// Event that is raised when the ticker adapter is started.
        /// </summary>
        public event EventHandler<EventArgs> Started;

        /// <summary>
        /// Raises the Started event to event subscribers.
        /// </summary>
        /// <remarks>
        /// Note to inheritors: You must call the base method in order to ensure that the event is raised and clean up
        /// work is properly performed.
        /// </remarks>
        /// <param name="e">Event arguments.</param>
        protected virtual void OnStarted(EventArgs e)
        {

            EventHandler<EventArgs> handlers = Started;

            // Invoke the event handlers
            if (handlers != null)
            {
                try
                {
                    handlers(this, e);
                }   // try
                catch (Exception)
                {
                    throw;
                    // Respond to exceptions in the handler
                    // or ignore them
                }   // catch ( Exception )
            }   // if

        }
        #endregion

        #region TickerAdded Event
        /// <summary>
        /// Event that is raised when a new ticker is added to the collection of <see cref="Tickers"/>.
        /// </summary>
        public event EventHandler<TickerAddedEventArgs> TickerAdded;

        /// <summary>
        /// Raises the TickerAdded event to event subscribers.
        /// </summary>
        /// <remarks>
        /// Note to inheritors: You must call the base method in order to ensure that the event is raised and clean up
        /// work is properly performed.
        /// </remarks>
        /// <param name="e">Event arguments.</param>
        protected virtual void OnTickerAdded(TickerAddedEventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, (MethodInvoker)delegate()
            {
                EventHandler<TickerAddedEventArgs> handlers = TickerAdded;

                // Invoke the event handlers
                if (handlers != null)
                {
                    try
                    {
                        handlers(this, e);
                    }   // try
                    catch (Exception)
                    {
                        throw;
                        // Respond to exceptions in the handler
                        // or ignore them
                    }   // catch ( Exception )
                }   // if
            });
        }
        #endregion

        #region SecurityAdded Event
        /// <summary>
        /// Event that is raised when a security is added to the <see cref="Securities"/>.
        /// </summary>
        public event EventHandler<SecurityAddedEventArgs> SecurityAdded;

        /// <summary>
        /// Raises the SecurityAdded event to event subscribers.
        /// </summary>
        /// <remarks>
        /// Note to inheritors: You must call the base method in order to ensure that the event is raised and clean up
        /// work is properly performed.
        /// </remarks>
        /// <param name="e">Event arguments.</param>
        protected virtual void OnSecurityAdded(SecurityAddedEventArgs e)
        {

            EventHandler<SecurityAddedEventArgs> handlers = SecurityAdded;

            // Invoke the event handlers
            if (handlers != null)
            {
                try
                {
                    handlers(this, e);
                }   // try
                catch (Exception)
                {
                    throw;
                    // Respond to exceptions in the handler
                    // or ignore them
                }   // catch ( Exception )
            }   // if

        }
        #endregion




        #region TickerInfo Class

        /// <summary>
        /// A class holding basic statistics about a ticker.
        /// </summary>
        public class TickerInfo
        {
            /// <summary>
            /// Initializes a new instance of the <see cref="TickerInfo"/>.
            /// </summary>
            /// <param name="security">The security that the ticker displays.</param>
            /// <param name="ticker">The ticker the info is for.</param>
            public TickerInfo(Security security, MiniTicker ticker)
            {
                if (security == null)
                    throw new ArgumentNullException("security");

                if (ticker == null)
                    throw new ArgumentNullException("ticker");

                Security = security;
                Ticker = ticker;
            }

            /// <summary>
            /// Gets the ticker that this tick info is for.
            /// </summary>
            public MiniTicker Ticker { get; private set; }

            public Security Security { get; private set; }
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Engineer
Switzerland Switzerland
Daniel is a former senior engineer in Technology and Research at the Office of the CTO at Microsoft, working on next generation systems.

Previously Daniel was a nine-time Microsoft MVP and co-founder of Outcoder, a Swiss software and consulting company.

Daniel is the author of Windows Phone 8 Unleashed and Windows Phone 7.5 Unleashed, both published by SAMS.

Daniel is the developer behind several acclaimed mobile apps including Surfy Browser for Android and Windows Phone. Daniel is the creator of a number of popular open-source projects, most notably Codon.

Would you like Daniel to bring value to your organisation? Please contact

Blog | Twitter


Xamarin Experts
Windows 10 Experts

Comments and Discussions