aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Tango.Integration/Diagnostics/DiagnosticsFilePlayer.cs
blob: 80a19c296e9be96125cedb6d0f44ffe79c886dab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;

namespace Tango.Scripting.Editors.Intellisense
{
    public class StructCompletionItem : CompletionItem
    {
        private static BitmapSource image = GetImage("struct.png");

        public override string Text => Name;
        public override CompletionItemPopupControl PopupControl => new StructCompletionItemPopup();
        public override BitmapSource Image => image;

        public String Name { get; set; }
        public String Namespace { get; set; }
    }
}
ef='#n314'>314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tango.BL.Entities;
using Tango.Core;
using Tango.Logging;
using Tango.PMR.Diagnostics;
using Tango.Serialization;

namespace Tango.Integration.Diagnostics
{
    /// <summary>
    /// Represents a Tango machine diagnostics file player capable of streaming diagnostics data frames.
    /// </summary>
    /// <seealso cref="Tango.Core.ExtendedObject" />
    /// <seealso cref="System.IDisposable" />
    public class DiagnosticsFilePlayer : ExtendedObject, IDisposable
    {
        private FileStream _dataFileStream; //Holds the data file stream.
        private DiagnosticsTimeCodeChannel _timeCodeChannel; //Holds the encapsulated time code data.
        private long _diagnosticsDataOffset; //Holds the actual starting position for the diagnostics packets.
        private Thread _playThread; //Holds the playing thread.
        private TaskCompletionSource<object> _stopTaskSource; //Holds the "Stop" async method completion source.

        #region Events

        /// <summary>
        /// Occurs when there is a new diagnostic frame is available.
        /// </summary>
        public event EventHandler<DataFileFrame> FrameReceived;

        #endregion

        #region Properties

        private bool _isLoaded;
        /// <summary>
        /// Gets a value indicating whether a diagnostics file is currently loaded and ready to be played.
        /// </summary>
        public bool IsLoaded
        {
            get { return _isLoaded; }
            private set { _isLoaded = value; RaisePropertyChangedAuto(); }
        }

        private bool _isPlaying;
        /// <summary>
        /// Gets a value indicating whether the player is in play mode (Played/Pause).
        /// </summary>
        public bool IsPlaying
        {
            get { return _isPlaying; }
            private set { _isPlaying = value; RaisePropertyChangedAuto(); }
        }

        private bool _isPaused;
        /// <summary>
        /// Gets or sets a value indicating whether the player is paused.
        /// </summary>
        public bool IsPaused
        {
            get { return _isPaused; }
            private set { _isPaused = value; RaisePropertyChangedAuto(); }
        }

        private TimeSpan _currentTime;
        /// <summary>
        /// Gets the current playing time.
        /// </summary>
        public TimeSpan CurrentTime
        {
            get { return _currentTime; }
            private set { _currentTime = value; RaisePropertyChanged(nameof(CurrentTime)); }
        }

        private TimeSpan _totalTime;
        /// <summary>
        /// Gets or sets the total playing time.
        /// </summary>
        public TimeSpan TotalTime
        {
            get { return _totalTime; }
            set { _totalTime = value; RaisePropertyChangedAuto(); }
        }

        private int _currentFrame;
        /// <summary>
        /// Gets or sets the current frame index.
        /// </summary>
        public int CurrentFrame
        {
            get { return _currentFrame; }
            set
            {
                _currentFrame = value;

                OnCurrentFrameChanged();

                RaisePropertyChanged(nameof(CurrentFrame));
            }
        }

        private long _totalFrames;
        /// <summary>
        /// Gets the total frames count.
        /// </summary>
        public long TotalFrames
        {
            get { return _totalFrames; }
            private set { _totalFrames = value; RaisePropertyChangedAuto(); }
        }

        private List<MachinesEvent> _machineEvents;
        /// <summary>
        /// Gets or sets the machine events.
        /// </summary>
        public List<MachinesEvent> MachineEvents
        {
            get { return _machineEvents; }
            set { _machineEvents = value; RaisePropertyChangedAuto(); }
        }

        private double _speed;
        /// <summary>
        /// Gets or sets the player speed (default 1.0).
        /// </summary>
        public double Speed
        {
            get { return _speed; }
            set { _speed = value; RaisePropertyChangedAuto(); }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="DiagnosticsFilePlayer"/> class.
        /// </summary>
        public DiagnosticsFilePlayer()
        {
            Speed = 1.0;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Loads the specified diagnostics file.
        /// </summary>
        /// <param name="fileName">Absolute file path.</param>
        /// <returns></returns>
        public async Task Load(String fileName)
        {
            Task task = new Task(() => 
            {
                try
                {
                    if (_dataFileStream != null)
                    {
                        _dataFileStream.Dispose();
                    }

                    _dataFileStream = new FileStream(fileName, FileMode.Open);

                    BinaryReader binaryReader = new BinaryReader(_dataFileStream);

                    int timeCodeDataSize = binaryReader.ReadInt32();
                    byte[] timeCodeData = binaryReader.ReadBytes(timeCodeDataSize);

                    BinaryDataSerializer serializer = new BinaryDataSerializer();
                    _timeCodeChannel = serializer.DeserializeFromBytes<DiagnosticsTimeCodeChannel>(timeCodeData);

                    _diagnosticsDataOffset = _dataFileStream.Position;

                    CurrentFrame = 0;
                    TotalFrames = _timeCodeChannel.Frames.Count;
                    TotalTime = TimeSpan.FromMilliseconds(_timeCodeChannel.Frames.Last().Milliseconds);

                    if (_timeCodeChannel.Events != null)
                    {
                        MachineEvents = _timeCodeChannel.Events.Select(x => x.ToMachineEvent()).ToList();
                    }

                    IsLoaded = true;
                }
                catch (Exception ex)
                {
                    if (_dataFileStream != null)
                    {
                        _dataFileStream.Dispose();
                    }

                    throw LogManager.Log(ex);
                }
            });

            task.Start();
            await task;
        }

        /// <summary>
        /// Seeks to the specified frame index.
        /// </summary>
        /// <param name="frameIndex">Index of the frame.</param>
        public void Seek(int frameIndex)
        {
            if (frameIndex < 0)
            {
                frameIndex = 0;
            }
            else if (frameIndex > TotalFrames - 1)
            {
                frameIndex = (int)(TotalFrames - 1);
            }

            CurrentFrame = frameIndex;
        }

        /// <summary>
        /// Starts playing the diagnostics file.
        /// </summary>
        /// <exception cref="InvalidOperationException">No diagnostics file is currently loaded.</exception>
        public void Play()
        {
            if (!IsLoaded) throw LogManager.Log(new InvalidOperationException("No diagnostics file is currently loaded."));

            IsPaused = false;

            if (!IsPlaying)
            {
                IsPlaying = true;
                _playThread = new Thread(PlayThreadMethod);
                _playThread.IsBackground = true;
                _playThread.Start();
            }
        }

        /// <summary>
        /// Stops this instance.
        /// </summary>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException">No diagnostics file is currently loaded.</exception>
        public Task Stop()
        {
            if (!IsLoaded) throw LogManager.Log(new InvalidOperationException("No diagnostics file is currently loaded."));

            if (IsPlaying)
            {
                _stopTaskSource = new TaskCompletionSource<object>();
                IsPlaying = false;
                IsPaused = false;
                return _stopTaskSource.Task;
            }
            else
            {
                return Task.FromResult(new object());
            }
        }

        /// <summary>
        /// Pauses the player.
        /// </summary>
        /// <exception cref="InvalidOperationException">No diagnostics file is currently loaded.</exception>
        public void Pause()
        {
            if (!IsLoaded) throw LogManager.Log(new InvalidOperationException("No diagnostics file is currently loaded."));

            if (IsPlaying)
            {
                IsPaused = true;
            }
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Called when the current frame has been changed
        /// </summary>
        protected void OnCurrentFrameChanged()
        {
            if (IsPlaying)
            {
                if (_currentFrame > _timeCodeChannel.Frames.Count - 1)
                {
                    _currentFrame = _timeCodeChannel.Frames.Count - 1;
                }

                if (_dataFileStream != null && _dataFileStream.CanSeek)
                {
                    _dataFileStream.Position = _diagnosticsDataOffset + _timeCodeChannel.Frames[_currentFrame].Position;
                }

                byte[] data = new byte[_timeCodeChannel.Frames[_currentFrame].FrameLength];
                _dataFileStream.Read(data, 0, data.Length);
                DataFileFrame frame = DataFileFrame.Parser.ParseFrom(data);
                OnFrameReceived(frame);
                data = null;
            }

            if (_timeCodeChannel != null)
            {
                CurrentTime = TimeSpan.FromMilliseconds(_timeCodeChannel.Frames[_currentFrame].Milliseconds);
            }
        }

        /// <summary>
        /// Raises the <see cref="FrameReceived"/> event.
        /// </summary>
        /// <param name="frame">The frame.</param>
        protected virtual void OnFrameReceived(DataFileFrame frame)
        {
            FrameReceived?.Invoke(this, frame);
        }

        #endregion

        #region Playing Thread

        /// <summary>
        /// Handles the playing thread.
        /// </summary>
        private void PlayThreadMethod()
        {
            while (IsPlaying)
            {
                if (!IsPaused)
                {
                    CurrentFrame++;

                    if (CurrentFrame >= TotalFrames - 1)
                    {
                        CurrentFrame = 0;
                    }
                }

                if (CurrentFrame > 0)
                {
                    double sleep = _timeCodeChannel.Frames[CurrentFrame].Milliseconds - _timeCodeChannel.Frames[CurrentFrame - 1].Milliseconds;
                    Thread.Sleep((int)(sleep / Speed));
                }
                else
                {
                    Thread.Sleep((int)(10d / Speed));
                }
            }

            CurrentFrame = 0;
            CurrentTime = TimeSpan.Zero;

            _stopTaskSource.SetResult(new object());
        }

        #endregion

        #region IDisposable

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            IsPlaying = false;

            if (_dataFileStream != null)
            {
                _dataFileStream.Dispose();
                _dataFileStream = null;
            }
        }

        #endregion

        #region Finalizer

        /// <summary>
        /// Finalizes an instance of the <see cref="DiagnosticsFilePlayer"/> class.
        /// </summary>
        ~DiagnosticsFilePlayer()
        {
            if (_dataFileStream != null)
            {
                _dataFileStream.Dispose();
            }
        }

        #endregion
    }
}