aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Graphics/ppc-module.png
blob: d9ce03e7bbc11ef3f108cae7d3078dc25ae5f878 (plain)
ofshex dumpascii
0000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 40 00 00 00 40 08 06 00 00 00 aa 69 71 .PNG........IHDR...@...@......iq
0020 de 00 00 00 04 73 42 49 54 08 08 08 08 7c 08 64 88 00 00 00 09 70 48 59 73 00 00 01 bb 00 00 01 .....sBIT....|.d.....pHYs.......
0040 bb 01 3a ec e3 e2 00 00 00 19 74 45 58 74 53 6f 66 74 77 61 72 65 00 77 77 77 2e 69 6e 6b 73 63 ..:.......tEXtSoftware.www.inksc
0060 61 70 65 2e 6f 72 67 9b ee 3c 1a 00 00 04 4d 49 44 41 54 78 9c ed 9b 4d 6f 1b 45 18 c7 ff 33 fb ape.org..<....MIDATx...Mo.E...3.
0080 92 d8 49 bc 8e e3 38 76 40 21 08 55 a5 88 1e 50 25 1a 2a 1c 50 d5 48 05 15 29 a9 e0 88 54 a9 9f ..I...8v@!.U...P%.*.P.H..)...T..
00a0 80 0b df a0 17 ae 7c 8b 1e 88 84 68 15 a9 80 20 41 b4 48 20 48 51 5b 8a 0f 81 56 a6 f1 5b e3 c8 ......|....h....A.H.HQ[...V..[..
00c0 ce da de 97 19 0e b4 6e 36 bb 6e b2 9b 38 43 bb fb 3b 79 9e 99 e7 f1 7f fe d2 8e 77 1f ad 09 e7 .......n6.n..8C..;y........w....
00e0 1c 61 86 8a 16 20 9a c8 00 d1 02 44 13 7a 03 e4 c7 1f ee 16 0a 9f 31 ce cf 80 b1 31 91 82 fa 0e .a.........D.z........1....1....
0100 a5 35 4a c8 d7 47 8f 1c f9 14 00 c8 ad 5b e5 61 90 ea 9a 65 9a 69 d1 da 0e 13 59 51 aa e0 e9 97 .5J..G.......[.a...e.i....YQ....
0120 29 95 37 ae 84 6d f3 00 60 99 66 9a ca 1b 57 64 cb 34 67 76 4e c6 0b 45 8c 5e bf 83 ce c4 28 ea ).7..m..`.f...Wd.4gvN..E.^....(.
0140 a7 5e 83 35 12 eb ce 11 ce 90 68 17 90 68 fd 89 e6 c0 34 36 e3 c7 c0 48 f7 4a 42 1b 26 7e b6 ff .^.5......h..h....46...H.JB.&~..
0160 c6 1a ab e1 84 34 85 63 34 0b 02 72 38 bb f2 89 65 9a 33 32 b3 6d 75 7b 70 f0 7e 05 2f 7d fe 65 .....4.c4..r8...e.32.mu{p.~./}.e
0180 77 3c 7c fb 1e fe fa 64 a1 3b 1e d5 6f 22 57 ff 16 00 90 68 15 a0 5a 1b 28 69 b3 dd f9 45 f3 37 w<|....d.;..o"W....h..Z.(i...E.7
01a0 fc 6a df 07 00 fc 6e 17 f1 b1 72 12 c7 a5 17 fa ba 91 a0 30 db 56 5d bf 02 da 4f 77 1d e3 d8 da .j....n...r........0.V]...Ow....
01c0 3a d4 52 bd 3b 4e ea b7 1d f3 db c7 6d 98 b8 69 17 1d f3 bf d8 f7 0e 44 6c bf 70 19 d0 9a 9e 70 :.R.;N......m..i.......Dl.p....p
01e0 8c ad 91 18 cc 74 e2 c9 bc 9a 73 ae 57 b3 dd cf 83 50 90 a5 09 c7 fc 14 4d 1d 88 d0 7e e1 32 a0 .....t....s.W....P......M...~.2.
0200 f1 c6 2b d8 7c f3 28 b8 22 c1 18 d7 50 fa 28 0f 2e 3d 59 56 1d 3e 81 e6 e0 34 00 40 57 27 51 4e ..+.|.(."...P.(..=YV.>...4.@W'QN
0220 9c 72 e4 9f 93 8f 63 92 6a a0 20 78 5d 9a c4 49 69 ba af 1b d8 2f 64 75 75 d5 f3 69 88 76 4c 30 .r....c.j..x]..Ii..../duu..i.vL0
0240 55 41 af f3 4b 62 1d d8 74 a0 67 e1 36 4c 0c 42 39 10 91 fd 44 ee 35 c1 06 9e 2e fe 69 9b 07 f0 UA..Kb..t.g.6L.B9...D.5.....i...
0260 4c 6c 1e 88 6e 85 21 8f 4d 64 77 5f f5 1c 23 13 f2 ff bc 49 39 2c 42 7f 09 84 de 00 d7 af 00 6d Ll..n.!.Mdw_..#....I9,B........m
0280 e8 88 5d bd 01 5a ad 7b ad ef 09 4b 25 d0 7a ff 2d 30 6d c8 11 6f 58 c0 72 8d 63 c3 f4 27 6c 54 ..]..Z.{...K%.z.-0m..oX.r.c..'lT
02a0 01 66 c7 08 46 76 28 a4 56 13 f1 d2 0f a0 e6 a6 3f 7d 6a 12 7a 26 0f 26 c7 1d 71 97 01 f1 c5 65 .f..Fv(.V.......?}j.z&.&..q....e
02c0 c4 be fa d1 9f da 47 10 bd 8d e6 c5 73 8e d8 b5 0a c7 77 d5 40 e5 d0 61 1c e7 73 ce 33 6a e8 9f ......G.....s.....w.@..a..s.3j..
02e0 6b 88 57 02 ea 63 06 1a 2f 7e e0 88 b9 2e 01 5a 7a 18 a8 38 00 48 eb ee dc 9a 11 b8 9c 67 ae dc k.W..c../~.....Zz..8.H.......g..
0300 a9 05 ae 27 79 e4 86 fe 0c 88 0c 10 2d 40 34 91 01 a2 05 88 26 32 40 b4 00 d1 44 06 88 16 20 9a ...'y.......-@4.....&2@...D.....
0320 c8 00 d1 02 44 e3 32 80 a5 12 5e eb f6 84 57 ae d6 b3 eb b8 3b 5e b9 b6 1a 5c 9f ad 68 ae 98 eb ....D.2...^...W.....;^...\..h...
0340 2b f4 f9 59 90 b6 01 a9 e2 ef 71 d8 1e d3 a0 7f f8 ae 2b 7e 26 43 60 70 8e 87 3e 1f 8a 52 ea 7f +..Y......q.......+~&C`p..>..R..
0360 b9 3b d9 ca cd 81 30 1b d4 f4 a9 4f 4d 41 cf 9e 76 c5 49 b1 54 0e f5 4b 42 d1 19 20 5a 80 68 dc .;....0....OMA..v.I.T..KB...Z.h.
0380 2d 31 4b 47 bc bc 02 6a f8 6c 89 29 1a f4 89 b7 c1 e4 61 67 bd cd 2d c4 96 6e 80 56 7d b6 b0 d2 -1KG...j.l.)......ag..-..n.V}...
03a0 1a 5a 67 67 3c 5b 6c df d7 38 ea 3e 5b 6c 29 05 78 27 4d 30 24 39 e3 2e 03 86 d6 bf 41 bc b4 ec .Zgg<[l..8.>[l).x'M0$9......A...
03c0 af fa 23 88 dd 42 63 6a c1 11 8b 2f 2e 23 76 f5 7a b0 7a 2d 03 cd 0b ef 39 62 4b 65 8e 95 80 4d ..#..Bcj.../.#v.z.z-....9bKe...M
03e0 a1 0e e3 58 d8 d1 62 73 5d 02 5e 6d a3 bd e2 d5 ae a2 95 8d c0 f5 bc 72 ab fb 68 b1 79 e5 86 fe ...X..bs].^m...........r..h.y...
0400 0c 88 0c 10 2d 40 34 91 01 a2 05 88 26 32 40 b4 00 d1 44 06 88 16 20 9a c8 00 d1 02 44 e3 32 c0 ....-@4.....&2@...D.........D.2.
0420 ab 6d b4 57 bc da 55 2c 39 12 b8 9e 57 ee 7e 5a 6c 49 8f 37 f7 64 00 0c db 8c d0 b3 a7 41 98 01 .m.W..U,9...W.~ZlI.7.d.......A..
0440 c9 f0 f7 9e 00 53 92 d8 ca cd b9 e2 ad f9 3c 88 61 fa 6f b1 8d 27 d1 9a cf bb e2 67 33 04 36 f7 .....S........<.a.o..'.....g3.6.
0460 ff c6 49 4a 05 e6 c6 5d 2d 36 46 8a eb e5 3b 20 78 d5 5f b9 e7 04 8e 3f 28 08 bf 2c 5a 87 30 08 ..IJ...]-6F...;.x._....?(..,Z.0.
0480 bf 4c eb d5 cc 25 00 4b a2 b5 08 60 a9 5e cd 5c 22 8f ff 39 fa a0 5c 3e cf 08 c9 13 20 b7 4b e2 .L...%.K...`.^.\"..9..\>......K.
04a0 33 0d 07 1e 50 ce 57 72 99 cc 17 00 40 a2 bf ce 86 9c c8 00 d1 02 44 13 7a 03 fe 05 55 c3 5a 71 3...P.Wr....@.........D.z...U.Zq
04c0 a8 cb 25 e0 00 00 00 00 49 45 4e 44 ae 42 60 82 ..%.....IEND.B`.
using Google.Protobuf.Collections;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using Tango.Core.Helpers;
using Tango.Editors;
using Tango.BL.Entities;
using Tango.Integration.Operation;
using Tango.MachineStudio.Common.Diagnostics;
using Tango.MachineStudio.Common.Notifications;
using Tango.MachineStudio.Common.StudioApplication;
using Tango.MachineStudio.Technician.Editors;
using Tango.MachineStudio.Technician.Project;
using Tango.MachineStudio.Technician.TechItems;
using Tango.PMR.Diagnostics;
using Tango.Settings;
using Tango.SharedUI;
using Tango.Integration.ExternalBridge;
using Tango.BL.Enumerations;
using Tango.BL;
using Tango.MachineStudio.Common.EventLogging;
using Tango.MachineStudio.Common;
using Tango.Core.Commands;
using Tango.MachineStudio.Technician.Helpers;
using Tango.MachineStudio.Technician.Models;
using Tango.Logging;
using Microsoft.WindowsAPICodePack.Dialogs;
using RealTimeGraphX;
using RealTimeGraphX.DataPoints;
using Tango.MachineStudio.Technician.Views;
using RealTimeGraphX.WPF;
using Tango.Core.ExtensionMethods;
using System.Diagnostics;
using Tango.BL.Builders;
using Tango.Core;

namespace Tango.MachineStudio.Technician.ViewModels
{
    /// <summary>
    /// Represents the MachineTechView View Model.
    /// </summary>
    /// <seealso cref="Tango.SharedUI.ViewModel" />
    /// <seealso cref="Tango.MachineStudio.Common.StudioApplication.IShutdownListener" />
    public class MachineTechViewVM : StudioViewModel
    {
        private class PackColor
        {
            public int Index { get; set; }
            public Color Color { get; set; }
            public String Name { get; set; }
        }

        private List<PropertyInfo> _diagnoticsMonitorsDataProperties;
        private IDiagnosticsFrameProvider _diagnosticsFrameProvider;
        private Dictionary<SingleGraphItem, TechGraphController> _singleControllers;
        private Dictionary<MultiGraphItem, TechGraphController> _multiControllers;
        private static object _elementsLock = new object();
        private String _lastTechProjectFile;
        private INotificationProvider _notification;
        private IEventLogger _eventLogger;
        private DateTime _lastDiagnosticsResponseUpdate;
        private const int MIN_DIAGNOSTICS_UPDATE_MILI = 500;
        private TechnicianModuleSettings _settings;

        private List<SingleTechRecordingData> _single_graphs_recordings;
        private List<MultiTechRecordingData> _multi_graph_recordings;
        private List<SingleTechRecordingData> _single_monitors_recordings;
        private List<MultiTechRecordingData> _multi_monitors_recordings;

        private DateTime _start_time = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0);
        private DateTime _last_time = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0);
        private Machine _machine;
        private List<PackColor> _packsColors;
        private int _diagnostics_index = 0;
        private DateTime _diagnosticsStartTime;
        private DateTime _diagnosticsNowTime;

        private ProducerConsumerQueue<StartDiagnosticsResponse> _framesQueue;
        private Thread _populateFramesThread;

        #region Properties

        private ObservableCollection<MachineTechTabVM> _tabs;
        /// <summary>
        /// Gets or sets the elements tabs.
        /// </summary>
        public ObservableCollection<MachineTechTabVM> Tabs
        {
            get { return _tabs; }
            set { _tabs = value; RaisePropertyChangedAuto(); }
        }

        private MachineTechTabVM _selectedTab;
        /// <summary>
        /// Gets or sets the selected tab.
        /// </summary>
        public MachineTechTabVM SelectedTab
        {
            get { return _selectedTab; }
            set
            {
                _selectedTab = value;
                RaisePropertyChangedAuto();

                foreach (var tab in Tabs.Where(x => x != _selectedTab))
                {
                    tab.IsSelected = false;
                }

                if (_selectedTab != null)
                {
                    _selectedTab.IsSelected = true;
                }

                EnableRenderingForSelectedTabGraphs();
            }
        }

        private ObservableCollection<TechItem> _availableTechItems;
        /// <summary>
        /// Gets or sets the available tech items.
        /// </summary>
        public ObservableCollection<TechItem> AvailableTechItems
        {
            get { return _availableTechItems; }
            set { _availableTechItems = value; RaisePropertyChangedAuto(); }
        }

        private TechItem _selectedTechItem;
        /// <summary>
        /// Gets or sets the selected available tech item.
        /// </summary>
        public TechItem SelectedTechItem
        {
            get { return _selectedTechItem; }
            set { _selectedTechItem = value; RaisePropertyChangedAuto(); }
        }

        /// <summary>
        /// Gets or sets the db adapter.
        /// </summary>
        public ObservablesStaticCollections Adapter { get; set; }

        /// <summary>
        /// Gets or sets the application manager.
        /// </summary>
        public IStudioApplicationManager ApplicationManager { get; set; }

        private IMachineOperator _machineOperator;
        /// <summary>
        /// Gets or sets the machine operator.
        /// </summary>
        public IMachineOperator MachineOperator
        {
            get { return _machineOperator; }
            set { _machineOperator = value; RaisePropertyChangedAuto(); }
        }

        private bool _disableRendering;
        /// <summary>
        /// Gets or sets a value indicating whether [disable rendering].
        /// </summary>
        public bool DisableRendering
        {
            get { return _disableRendering; }
            set { _disableRendering = value; RaisePropertyChangedAuto(); OnDisableRenderingChanged(); }
        }

        private bool _hideMenu;
        /// <summary>
        /// Gets or sets a value indicating whether [hide menu].
        /// </summary>
        public bool HideMenu
        {
            get { return _hideMenu; }
            set { _hideMenu = value; RaisePropertyChangedAuto(); }
        }

        private StartDiagnosticsResponse _currentDiagnosticsResponse;
        /// <summary>
        /// Gets or sets the current diagnostics response.
        /// </summary>
        public StartDiagnosticsResponse CurrentDiagnosticsResponse
        {
            get { return _currentDiagnosticsResponse; }
            set { _currentDiagnosticsResponse = value; RaisePropertyChanged(nameof(CurrentDiagnosticsResponse)); }
        }

        private int _currentDiagnosticsResponseSize;
        /// <summary>
        /// Gets or sets the size of the current diagnostics response.
        /// </summary>
        /// <value>
        /// The size of the current diagnostics response.
        /// </value>
        public int CurrentDiagnosticsResponseSize
        {
            get { return _currentDiagnosticsResponseSize; }
            set { _currentDiagnosticsResponseSize = value; RaisePropertyChanged(nameof(CurrentDiagnosticsResponseSize)); }
        }

        private int _graphsDurationSeconds;
        /// <summary>
        /// Gets or sets the graphs duration seconds.
        /// </summary>
        public int GraphsDurationSeconds
        {
            get { return _graphsDurationSeconds; }
            set { _graphsDurationSeconds = value; RaisePropertyChangedAuto(); }
        }

        private int _tempGraphsDurationSeconds;
        /// <summary>
        /// Gets or sets the temporary graphs duration seconds.
        /// </summary>
        public int TempGraphsDurationSeconds
        {
            get { return _tempGraphsDurationSeconds; }
            set { _tempGraphsDurationSeconds = value; RaisePropertyChangedAuto(); }
        }



        #endregion

        #region Commands

        /// <summary>
        /// Gets or sets the save as project command.
        /// </summary>
        public RelayCommand SaveAsProjectCommand { get; set; }

        /// <summary>
        /// Gets or sets the save project command.
        /// </summary>
        public RelayCommand SaveProjectCommand { get; set; }

        /// <summary>
        /// Gets or sets the open project command.
        /// </summary>
        public RelayCommand OpenProjectCommand { get; set; }

        /// <summary>
        /// Gets or sets the synchronize hardware configuration command.
        /// </summary>
        public RelayCommand SyncHardwareConfigurationCommand { get; set; }

        /// <summary>
        /// Gets or sets the upload hardware configuration command.
        /// </summary>
        public RelayCommand UploadHardwareConfigurationCommand { get; set; }

        /// <summary>
        /// Gets or sets the reset hardware configuration command.
        /// </summary>
        public RelayCommand ResetHardwareConfigurationCommand { get; set; }

        /// <summary>
        /// Gets or sets the update graphs duration command.
        /// </summary>
        public RelayCommand UpdateGraphsDurationCommand { get; set; }

        /// <summary>
        /// Gets or sets the add tab command.
        /// </summary>
        public RelayCommand AddTabCommand { get; set; }

        /// <summary>
        /// Gets or sets the remove tab command.
        /// </summary>
        public RelayCommand<MachineTechTabVM> RemoveTabCommand { get; set; }

        /// <summary>
        /// Gets or sets the new project command.
        /// </summary>
        public RelayCommand NewProjectCommand { get; set; }

        /// <summary>
        /// Gets or sets the rename tab command.
        /// </summary>
        public RelayCommand RenameTabCommand { get; set; }

        /// <summary>
        /// Gets or sets the import project tabs command.
        /// </summary>
        /// <value>
        /// The import project tabs command.
        /// </value>
        public RelayCommand ImportProjectTabsCommand { get; set; }

        /// <summary>
        /// Gets or sets the upload partial hardware configuration command.
        /// </summary>
        public RelayCommand UploadPartialHardwareConfigurationCommand { get; set; }

        /// <summary>
        /// Gets or sets the reset command.
        /// </summary>
        public RelayCommand ResetGraphsCommand { get; set; }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MachineTechViewVM"/> class.
        /// </summary>
        /// <param name="applicationManager">The application manager.</param>
        /// <param name="notificationProvider">The notification provider.</param>
        public MachineTechViewVM(IStudioApplicationManager applicationManager, INotificationProvider notificationProvider, IDiagnosticsFrameProvider diagnosticsFrameProvider, IEventLogger eventLogger)
        {
            _framesQueue = new ProducerConsumerQueue<StartDiagnosticsResponse>();

            Tabs = new ObservableCollection<MachineTechTabVM>();
            Tabs.Add(new MachineTechTabVM() { IsSelected = true, Name = "Untitled" });
            SelectedTab = Tabs.First();

            _settings = SettingsManager.Default.GetOrCreate<TechnicianModuleSettings>();

            _single_graphs_recordings = new List<SingleTechRecordingData>();
            _multi_graph_recordings = new List<MultiTechRecordingData>();

            _single_monitors_recordings = new List<SingleTechRecordingData>();
            _multi_monitors_recordings = new List<MultiTechRecordingData>();

            GraphsDurationSeconds = _settings.GraphsDuration;
            TempGraphsDurationSeconds = GraphsDurationSeconds;

            _notification = notificationProvider;
            _eventLogger = eventLogger;
            _singleControllers = new Dictionary<SingleGraphItem, TechGraphController>();
            _multiControllers = new Dictionary<MultiGraphItem, TechGraphController>();
            AvailableTechItems = TechItem.GetAvailableTechItems().ToObservableCollection();
            SelectedTechItem = AvailableTechItems.FirstOrDefault();
            _diagnoticsMonitorsDataProperties = typeof(DiagnosticsMonitors).GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList();
            ApplicationManager = applicationManager;
            ApplicationManager.ConnectedMachineChanged += ApplicationManager_ConnectedMachineChanged;

            Adapter = ObservablesStaticCollections.Instance;

            OpenProjectCommand = new RelayCommand(OpenProject);
            SaveAsProjectCommand = new RelayCommand(SaveAsProject);
            SaveProjectCommand = new RelayCommand(SaveProject);

            _lastTechProjectFile = _settings.LastTechProjectFile;

            if (File.Exists(_lastTechProjectFile))
            {
                try
                {
                    OpenProjectFile(_lastTechProjectFile);
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Error loading last project file.");
                }
            }

            _diagnosticsFrameProvider = diagnosticsFrameProvider;
            _diagnosticsFrameProvider.FrameReceived += DiagnosticsFrameProvider_FrameReceived;

            UploadHardwareConfigurationCommand = new RelayCommand(UploadHardwareConfiguration);
            SyncHardwareConfigurationCommand = new RelayCommand(SyncHardwareConfiguration);
            ResetHardwareConfigurationCommand = new RelayCommand(() => ResetHardwareConfiguration());
            UpdateGraphsDurationCommand = new RelayCommand(() =>
            {
                GraphsDurationSeconds = TempGraphsDurationSeconds;
                _settings.GraphsDuration = GraphsDurationSeconds;
                ClearAllGraphs();
            });

            AddTabCommand = new RelayCommand(() => AddNewTab());
            RemoveTabCommand = new RelayCommand<MachineTechTabVM>(RemoveTab);
            NewProjectCommand = new RelayCommand(CreateNewProject);
            RenameTabCommand = new RelayCommand(RenameTab);
            ImportProjectTabsCommand = new RelayCommand(ImportProjectTabs);
            UploadPartialHardwareConfigurationCommand = new RelayCommand(UploadPartialHardwareConfiguration);
            ResetGraphsCommand = new RelayCommand(ClearAllGraphs);
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Applications the manager connected machine changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="machine">The machine.</param>
        private async void ApplicationManager_ConnectedMachineChanged(object sender, IExternalBridgeClient machine)
        {
            MachineOperator = machine;

            if (MachineOperator != null)
            {
                ResetHardwareConfiguration(false);
            }

            ClearAllGraphs();

            if (machine != null)
            {
                try
                {
                    using (ObservablesContext db = ObservablesContext.CreateDefault())
                    {
                        _machine = await new MachineBuilder(db).Set(machine.Machine.Guid).WithConfiguration().BuildAsync();

                        _packsColors = new List<PackColor>();

                        foreach (var pack in _machine.Configuration.NoneEmptyIdsPacks.OrderBy(x => x.PackIndex).ToList())
                        {
                            PackColor pc = new PackColor();
                            pc.Index = pack.PackIndex;
                            pc.Name = pack.LiquidType.Name;

                            if (pack.LiquidType.LiquidTypeColor == Colors.Black)
                            {
                                pc.Color = Colors.Gray;
                            }
                            else if (pack.LiquidType.LiquidTypeColor == Colors.Transparent)
                            {
                                pc.Color = Colors.White;
                            }
                            else
                            {
                                pc.Color = pack.LiquidType.LiquidTypeColor;
                            }

                            _packsColors.Add(pc);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, LogCategory.Warning, "Error loading the connected machine details from db.");
                }
            }
        }

        /// <summary>
        /// Handles the diagnostics frame provider data.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="response">The response.</param>
        private void DiagnosticsFrameProvider_FrameReceived(object sender, StartDiagnosticsResponse response)
        {
            _framesQueue.BlockEnqueue(response);
        }

        #endregion

        #region Populate Diagnostics Data

        private void PopulateFramesThreadMethod()
        {
            while (!ApplicationManager.IsShuttingDown)
            {
                var frame = _framesQueue.BlockDequeue();
                PopulateDiagnosticsData(frame);
            }
        }

        /// <summary>
        /// Populates the diagnostics data to the proper elements.
        /// </summary>
        /// <param name="data">The data.</param>
        private void PopulateDiagnosticsData(StartDiagnosticsResponse data)
        {
            if (data.Monitors == null || _machineOperator == null || _machineOperator.DeviceInformation == null) return;

            _diagnostics_index++;
            uint interval = _machineOperator.DeviceInformation.DiagnosticsInterval;

            TimeSpan delta_base = DateTime.Now - _start_time;
            TimeSpan delta = (DateTime.Now - _last_time);
            _last_time = DateTime.Now;

            if (data.ElapsedMilli > 0)
            {
                _diagnosticsNowTime = DateTime.ParseExact(data.DateTime, "MM/dd/yyyy HH:mm:ss.fff", null); //_diagnosticsNowTime.Add(TimeSpan.FromMilliseconds(data.ElapsedMilli));
                //var now = _diagnosticsStartTime.Add(TimeSpan.FromMilliseconds(interval * _diagnostics_index));
                //Debug.WriteLine("DeltaBase Before: " + delta_base);
                delta_base = _diagnosticsNowTime - _start_time;
                //Debug.WriteLine("DeltaBase After: " + delta_base);

                //Debug.WriteLine("Delta Before: " + delta);
                delta = TimeSpan.FromMilliseconds(data.ElapsedMilli);
                //Debug.WriteLine("Delta After: " + delta);
            }

            double delta_mili = delta.TotalMilliseconds;

            if (DateTime.Now > _lastDiagnosticsResponseUpdate.AddMilliseconds(MIN_DIAGNOSTICS_UPDATE_MILI))
            {
                CurrentDiagnosticsResponse = data;
                _lastDiagnosticsResponseUpdate = DateTime.Now;
                CurrentDiagnosticsResponseSize = data.CalculateSize();
            }

            foreach (var sr in _single_monitors_recordings)
            {
                var techMonitor = (sr.Tag as TechMonitor);

                var prop = _diagnoticsMonitorsDataProperties.SingleOrDefault(x => x.Name == techMonitor.Name);

                if (prop != null)
                {
                    var points = GetDataArray(techMonitor, prop.GetValue(data.Monitors));

                    if (points.Count > 0)
                    {
                        sr.PushData(points, delta_base, delta);
                    }
                }
            }

            foreach (var mr in _multi_monitors_recordings)
            {
                var techMonitor = (mr.Tag as TechMonitor);

                var prop = _diagnoticsMonitorsDataProperties.SingleOrDefault(x => x.Name == techMonitor.Name);

                if (prop != null)
                {
                    var points = GetDataMatrix(techMonitor, prop.GetValue(data.Monitors));
                    if (points.Count > 0)
                    {
                        mr.PushData(points, delta_base, delta);
                    }
                }
            }

            lock (_elementsLock)
            {
                var elements = Tabs.SelectMany(x => x.Elements).ToList();
                var selectedTabElements = SelectedTab.Elements.ToList();

                //Can be updated only when visible
                foreach (var item in selectedTabElements.Select(x => x.HostedElement as TechItem))
                {
                    if (item.GetType() == typeof(MonitorItem))
                    {
                        MonitorItem monitorItem = item as MonitorItem;

                        if (DateTime.Now > monitorItem.LastUpdateTime.AddMilliseconds(monitorItem.UpdateInterval))
                        {
                            var prop = _diagnoticsMonitorsDataProperties.SingleOrDefault(x => x.Name == monitorItem.TechMonitor.Name);

                            if (prop != null)
                            {
                                monitorItem.Value = GetDataLastValue(monitorItem.TechMonitor, prop.GetValue(data.Monitors));
                            }
                        }
                    }
                    else if (item.GetType() == typeof(MeterItem))
                    {
                        MeterItem meterItem = item as MeterItem;

                        if (DateTime.Now > meterItem.LastUpdateTime.AddMilliseconds(meterItem.UpdateInterval))
                        {
                            var prop = _diagnoticsMonitorsDataProperties.SingleOrDefault(x => x.Name == meterItem.TechMonitor.Name);

                            if (prop != null)
                            {
                                meterItem.Value = GetDataLastValue(meterItem.TechMonitor, prop.GetValue(data.Monitors));
                            }
                        }
                    }
                    else if (item.GetType() == typeof(DigitalOutItem))
                    {
                        DigitalOutItem digitalOutItem = item as DigitalOutItem;

                        var digitalPin = data.DigitalInterfaceStates.SingleOrDefault(x => x.InterfaceIO == (InterfaceIOs)digitalOutItem.TechIo.Code);

                        if (digitalPin != null)
                        {
                            digitalOutItem.EffectiveValue = digitalPin.Value;
                        }
                    }
                    else if (item.GetType() == typeof(DigitalInItem))
                    {
                        DigitalInItem digitalInItem = item as DigitalInItem;

                        var digitalPin = data.DigitalInterfaceStates.SingleOrDefault(x => x.InterfaceIO == (InterfaceIOs)digitalInItem.TechIo.Code);

                        if (digitalPin != null)
                        {
                            digitalInItem.Value = digitalPin.Value;
                        }
                    }
                    else if (item.GetType() == typeof(HeaterItem))
                    {
                        HeaterItem heaterItem = item as HeaterItem;

                        var heaterState = data.HeatersStates.SingleOrDefault(x => x.HeaterType == (HeaterType)heaterItem.TechHeater.Code);

                        if (heaterState != null)
                        {
                            heaterItem.HeaterState = heaterState;
                        }
                    }
                    else if (item.GetType() == typeof(ValveItem))
                    {
                        ValveItem valveItem = item as ValveItem;

                        var valveState = data.ValvesStates.SingleOrDefault(x => x.ValveType == (ValveType)valveItem.TechValve.Code);

                        if (valveState != null)
                        {
                            valveItem.EffectiveState = valveState.State;
                        }
                    }
                    else if (item.GetType() == typeof(BlowerItem))
                    {
                        BlowerItem blowerItem = item as BlowerItem;

                        if (data.Monitors.BlowerVoltage.Count > 0)
                        {
                            blowerItem.EffectiveActive = data.Monitors.BlowerVoltage.Last() > 0;
                        }
                    }
                    else if (item.GetType() == typeof(ControllerItem))
                    {
                        ControllerItem controllerItem = item as ControllerItem;

                        if (DateTime.Now > controllerItem.LastUpdateTime.AddMilliseconds(controllerItem.UpdateInterval))
                        {
                            var componentState = data.ComponentsStates.SingleOrDefault(x => (int)x.Component == controllerItem.TechController.Code);

                            if (componentState != null)
                            {
                                controllerItem.EffectiveValue = componentState.Value;
                            }
                        }
                    }
                }

                //Must be updated all the time.
                foreach (var item in elements.Select(x => x.HostedElement as TechItem))
                {
                    if (item.GetType() == typeof(SingleGraphItem))
                    {
                        SingleGraphItem graphItem = item as SingleGraphItem;

                        var prop = _diagnoticsMonitorsDataProperties.SingleOrDefault(x => x.Name == graphItem.TechMonitor.Name);

                        if (prop != null)
                        {
                            TechGraphController controller = null;

                            if (_singleControllers.TryGetValue(graphItem, out controller))
                            {
                                var points = GetDataArray(graphItem.TechMonitor, prop.GetValue(data.Monitors));

                                if (points.Count > 0)
                                {
                                    List<TimeSpanDataPoint> times = new List<TimeSpanDataPoint>();
                                    var dPoints = points.Select(x => new DoubleDataPoint(x)).ToList();

                                    for (int i = 0; i < points.Count; i++)
                                    {
                                        times.Add(delta_base.Add(TimeSpan.FromMilliseconds((delta_mili / points.Count) * i)));
                                    }

                                    controller.PushData(times, dPoints);

                                    var _graph_recording = _single_graphs_recordings.SingleOrDefault(x => x.Tag == graphItem);
                                    if (_graph_recording != null)
                                    {
                                        _graph_recording.PushData(points, delta_base, delta);
                                    }
                                }
                            }
                        }
                    }
                    else if (item.GetType() == typeof(MultiGraphItem))
                    {
                        MultiGraphItem graphItem = item as MultiGraphItem;

                        var prop = _diagnoticsMonitorsDataProperties.SingleOrDefault(x => x.Name == graphItem.TechMonitor.Name);

                        if (prop != null)
                        {
                            TechGraphController controller = null;

                            if (_multiControllers.TryGetValue(graphItem, out controller))
                            {
                                var points = GetDataMatrix(graphItem.TechMonitor, prop.GetValue(data.Monitors));

                                if (points.Count > 0)
                                {
                                    List<TimeSpanDataPoint> times = new List<TimeSpanDataPoint>();
                                    var dPoints = points.Select(x => new List<DoubleDataPoint>(x.Select(y => new DoubleDataPoint(y)))).ToList();

                                    for (int i = 0; i < points[0].Count; i++)
                                    {
                                        times.Add(delta_base.Add(TimeSpan.FromMilliseconds((delta_mili / points[0].Count) * i)));
                                    }

                                    List<List<TimeSpanDataPoint>> timesMat = new List<List<TimeSpanDataPoint>>();

                                    for (int i = 0; i < controller.DataSeriesCollection.Count; i++)
                                    {
                                        timesMat.Add(times);
                                    }

                                    try
                                    {
                                        if (_packsColors != null)
                                        {
                                            foreach (var pack in _packsColors)
                                            {
                                                controller.DataSeriesCollection[pack.Index].Stroke = pack.Color;
                                                controller.DataSeriesCollection[pack.Index].Name = pack.Name;
                                            }
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        Debug.WriteLine($"Error assigning multi graph colors.\n{ex.ToString()}");
                                    }

                                    controller.PushData(timesMat, dPoints);
                                }

                                var _graph_recording = _multi_graph_recordings.SingleOrDefault(x => x.Tag == graphItem);
                                if (_graph_recording != null)
                                {
                                    _graph_recording.PushData(points, delta_base, delta);
                                }
                            }
                        }
                    }
                }
            }
        }

        #endregion

        #region Private Methods

        private void EnableRenderingForSelectedTabGraphs()
        {
            if (SelectedTab != null)
            {
                lock (_elementsLock)
                {
                    var elements = Tabs.SelectMany(x => x.Elements).ToList();

                    foreach (var element in elements)
                    {
                        if (SelectedTab.Elements.Contains(element))
                        {
                            var item = element.HostedElement as TechItem;

                            if (item != null)
                            {
                                if (item.GetType() == typeof(SingleGraphItem))
                                {
                                    SingleGraphItem graphItem = item as SingleGraphItem;

                                    TechGraphController controller = null;

                                    if (_singleControllers.TryGetValue(graphItem, out controller))
                                    {
                                        controller.DisableRendering = false;
                                    }
                                }
                                else if (item.GetType() == typeof(MultiGraphItem))
                                {
                                    MultiGraphItem graphItem = item as MultiGraphItem;

                                    TechGraphController controller = null;

                                    if (_multiControllers.TryGetValue(graphItem, out controller))
                                    {
                                        controller.DisableRendering = false;
                                    }
                                }
                            }
                        }
                        else
                        {
                            var item = element.HostedElement as TechItem;

                            if (item != null)
                            {
                                if (item.GetType() == typeof(SingleGraphItem))
                                {
                                    SingleGraphItem graphItem = item as SingleGraphItem;

                                    TechGraphController controller = null;

                                    if (_singleControllers.TryGetValue(graphItem, out controller))
                                    {
                                        controller.DisableRendering = true;
                                    }
                                }
                                else if (item.GetType() == typeof(MultiGraphItem))
                                {
                                    MultiGraphItem graphItem = item as MultiGraphItem;

                                    TechGraphController controller = null;

                                    if (_multiControllers.TryGetValue(graphItem, out controller))
                                    {
                                        controller.DisableRendering = true;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Gets the last data point from a protobuf repeated field.
        /// </summary>
        /// <param name="monitor">The monitor.</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        private double GetDataLastValue(TechMonitor monitor, object value)
        {
            if (!monitor.MultiChannel)
            {
                RepeatedField<double> arr = value as RepeatedField<double>;
                return arr.LastOrDefault();
            }
            else
            {
                RepeatedField<DoubleArray> arr = value as RepeatedField<DoubleArray>;
                return arr.Last().Data.Last();
            }
        }

        /// <summary>
        /// Gets the data array from a protobuf repeated field.
        /// </summary>
        /// <param name="monitor">The monitor.</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        private List<double> GetDataArray(TechMonitor monitor, object value)
        {
            return (value as RepeatedField<double>).ToList();
        }

        /// <summary>
        /// Gets the data matrix from a protobuf repeated field of <see cref="DoubleArray"/>.
        /// </summary>
        /// <param name="monitor">The monitor.</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        private List<List<double>> GetDataMatrix(TechMonitor monitor, object value)
        {
            DoubleArray[] arrayOfDoubles = Enumerable.ToArray(value as IEnumerable<DoubleArray>);
            return arrayOfDoubles.Select(x => x.Data.ToList()).ToList();
        }

        #endregion

        #region Virtual Methods

        /// <summary>
        /// Called when the disable rendering has been changed
        /// </summary>
        protected virtual void OnDisableRenderingChanged()
        {
            //foreach (var controller in _singleControllers)
            //{
            //    controller.Value.ChangeRenderMode(!DisableRendering);
            //}

            //foreach (var controller in _multiControllers)
            //{
            //    controller.Value.ChangeRenderMode(!DisableRendering);
            //}
        }

        #endregion

        #region Add/Remove Element

        /// <summary>
        /// Creates a new tech element by the specified bounds and the current selected element.
        /// </summary>
        /// <param name="bounds">The bounds.</param>
        public void CreateElement(Rect bounds)
        {
            CreateElement(SelectedTechItem, bounds);
        }

        /// <summary>
        /// Creates a new tech element by the specified tech item instance and bounds.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="bounds">The bounds.</param>
        private void CreateElement(TechItem item, Rect bounds)
        {
            if (item is MonitorItem)
            {
                CreateElement<MonitorElementEditor, MonitorItem, TechMonitor>(bounds, Adapter.TechMonitors.Where(x => !x.MultiChannel).FirstOrDefault());
            }
            else if (item is MeterItem)
            {
                CreateElement<MeterElementEditor, MeterItem, TechMonitor>(bounds, Adapter.TechMonitors.Where(x => !x.MultiChannel).FirstOrDefault());
            }
            else if (item is HeaterItem)
            {
                var editor = CreateElement<HeaterElementEditor, HeaterItem, TechHeater>(bounds, Adapter.TechHeaters.FirstOrDefault());
                InitTechHeater(editor.HeaterItem);
            }
            else if (item is ValveItem)
            {
                var editor = CreateElement<ValveElementEditor, ValveItem, TechValve>(bounds, Adapter.TechValves.FirstOrDefault());
                InitTechValveItem(editor.ValveItem);
            }
            else if (item is SingleGraphItem)
            {
                var editor = CreateElement<SingleGraphElementEditor, SingleGraphItem, TechMonitor>(bounds, Adapter.TechMonitors.Where(x => !x.MultiChannel).FirstOrDefault());
                InitSingleGraphitem(editor.GraphItem, editor);
            }
            else if (item is MultiGraphItem)
            {
                var editor = CreateElement<MultiGraphElementEditor, MultiGraphItem, TechMonitor>(bounds, Adapter.TechMonitors.Where(x => x.MultiChannel).FirstOrDefault());
                InitMultiGraphItem(editor.GraphItem, editor);
            }
            else if (item is MotorItem)
            {
                var editor = CreateElement<MotorElementEditor, MotorItem, HardwareMotorType>(bounds, Adapter.HardwareMotorTypes.FirstOrDefault());
                InitMotorItem(editor.MotorItem);
            }
            else if (item is DispenserItem)
            {
                var editor = CreateElement<DispenserElementEditor, DispenserItem, TechDispenser>(bounds, Adapter.TechDispensers.FirstOrDefault());
                InitDispenserItem(editor.DispenserItem);
            }
            else if (item is ThreadMotionItem)
            {
                var editor = CreateElement<ThreadMotionElementEditor, ThreadMotionItem, object>(bounds, null);
                InitThreadMotionItem(editor.ThreadMotionItem);
            }
            else if (item is MotorGroupItem)
            {
                var editor = CreateElement<MotorGroupElementEditor, MotorGroupItem, object>(bounds, null);
                InitMotorGroupItem(editor.MotorGroupItem);
            }
            else if (item is DigitalOutItem)
            {
                var editor = CreateElement<DigitalOutElementEditor, DigitalOutItem, TechIo>(bounds, Adapter.TechIos.Where(x => x.Type == IOType.DigitalOutput.ToInt32()).FirstOrDefault());
                InitDigitalOutItem(editor.DigitalOutItem);
            }
            else if (item is DigitalInItem)
            {
                CreateElement<DigitalInElementEditor, DigitalInItem, TechIo>(bounds, Adapter.TechIos.Where(x => x.Type == IOType.DigitalInput.ToInt32()).FirstOrDefault());
            }
            else if (item is ControllerItem)
            {
                var editor = CreateElement<ControllerElementEditor, ControllerItem, TechController>(bounds, Adapter.TechControllers.FirstOrDefault());
                InitControllerItem(editor.ControllerItem);
            }
            else if (item is PidItem)
            {
                CreateElement<PidElementEditor, PidItem, HardwarePidControlType>(bounds, Adapter.HardwarePidControlTypes.FirstOrDefault());
            }
            else if (item is WinderItem)
            {
                CreateElement<WinderElementEditor, WinderItem, HardwareWinderType>(bounds, Adapter.HardwareWinderTypes.FirstOrDefault());
            }
            else if (item is DancerItem)
            {
                CreateElement<DancerElementEditor, DancerItem, HardwareDancerType>(bounds, Adapter.HardwareDancerTypes.FirstOrDefault());
            }
            else if (item is SpeedSensorItem)
            {
                CreateElement<SpeedSensorElementEditor, SpeedSensorItem, HardwareSpeedSensorType>(bounds, Adapter.HardwareSpeedSensorTypes.FirstOrDefault());
            }
            else if (item is BlowerItem)
            {
                var editor = CreateElement<BlowerElementEditor, BlowerItem, HardwareBlowerType>(bounds, Adapter.HardwareBlowerTypes.FirstOrDefault());
                InitBlowerItem(editor.BlowerItem);
            }
            else if (item is BreakSensorItem)
            {
                CreateElement<BreakSensorElementEditor, BreakSensorItem, HardwareBreakSensorType>(bounds, Adapter.HardwareBreakSensorTypes.FirstOrDefault());
            }
            else if (item is ProcessParametersItem)
            {
                var editor = CreateElement<ProcessParametersElementEditor, ProcessParametersItem, object>(bounds, null);
                InitProcessParameterItem(editor.ProcessParametersItem);
            }
            else if (item is JobRunnerItem)
            {
                var editor = CreateElement<JobRunnerElementEditor, JobRunnerItem, object>(bounds, null);
                InitJobRunnerItem(editor.JobRunnerItem);
            }
            else if (item is TextItem)
            {
                CreateElement<TextElementEditor, TextItem, object>(bounds, null);
            }
            else if (item is MonitorRecorderItem)
            {
                var editor = CreateElement<MonitorRecorderElementEditor, MonitorRecorderItem, object>(bounds, null);
                InitMonitorRecorderItem(editor.MonitorRecorderItem);
            }
        }

        /// <summary>
        /// Creates a new element by the specified editor type, tech item type, bounds and tech item constructor value.
        /// </summary>
        /// <typeparam name="Editor">The type of the editor.</typeparam>
        /// <typeparam name="Tech">The type of the tech.</typeparam>
        /// <typeparam name="Value">The type of the value.</typeparam>
        /// <param name="bounds">The bounds.</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        private Editor CreateElement<Editor, Tech, Value>(Rect bounds, Value value) where Editor : IElementEditor where Tech : TechItem
        {
            TechItem item = Activator.CreateInstance(typeof(Tech), new object[] { value }) as TechItem;
            IElementEditor editor = Activator.CreateInstance(typeof(Editor), new object[] { ((Tech)item), bounds }) as IElementEditor;
            SelectedTab.Elements.Add(editor);
            return (Editor)editor;
        }

        /// <summary>
        /// Creates a new element by the specified editor type and tech item instance.
        /// </summary>
        /// <typeparam name="Editor">The type of the editor.</typeparam>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        private Editor CreateElement<Editor>(TechItem item) where Editor : IElementEditor
        {
            IElementEditor editor = Activator.CreateInstance(typeof(Editor), new object[] { item, item.GetBounds() }) as IElementEditor;
            SelectedTab.Elements.Add(editor);
            return (Editor)editor;
        }

        /// <summary>
        /// Adds a new tech item.
        /// </summary>
        /// <param name="item">The item.</param>
        private void AddTechItem(TechItem item)
        {
            if (item is MonitorItem)
            {
                (item as MonitorItem).TechMonitor = Adapter.TechMonitors.Where(x => !x.MultiChannel).FirstOrDefault(x => x.Guid == item.ItemGuid);
                CreateElement<MonitorElementEditor>(item);
            }
            else if (item is HeaterItem)
            {
                (item as HeaterItem).TechHeater = Adapter.TechHeaters.FirstOrDefault(x => x.Guid == item.ItemGuid);
                CreateElement<HeaterElementEditor>(item);
            }
            else if (item is ValveItem)
            {
                (item as ValveItem).TechValve = Adapter.TechValves.FirstOrDefault(x => x.Guid == item.ItemGuid);
                var editor = CreateElement<ValveElementEditor>(item);
                InitTechValveItem(editor.ValveItem);
            }
            else if (item is MeterItem)
            {
                (item as MeterItem).TechMonitor = Adapter.TechMonitors.Where(x => !x.MultiChannel).FirstOrDefault(x => x.Guid == item.ItemGuid);
                CreateElement<MeterElementEditor>(item);
            }
            else if (item is SingleGraphItem)
            {
                (item as SingleGraphItem).TechMonitor = Adapter.TechMonitors.Where(x => !x.MultiChannel).FirstOrDefault(x => x.Guid == item.ItemGuid);
                var editor = CreateElement<SingleGraphElementEditor>(item);
                InitSingleGraphitem(editor.GraphItem, editor);
            }
            else if (item is MultiGraphItem)
            {
                (item as MultiGraphItem).TechMonitor = Adapter.TechMonitors.Where(x => x.MultiChannel).FirstOrDefault(x => x.Guid == item.ItemGuid);
                var editor = CreateElement<MultiGraphElementEditor>(item);
                InitMultiGraphItem(editor.GraphItem, editor);
            }
            else if (item is MotorItem)
            {
                (item as MotorItem).HardwareMotorType = Adapter.HardwareMotorTypes.FirstOrDefault(x => x.Guid == item.ItemGuid);
                var editor = CreateElement<MotorElementEditor>(item);
                InitMotorItem(editor.MotorItem);
            }
            else if (item is DispenserItem)
            {
                (item as DispenserItem).TechDispenser = Adapter.TechDispensers.FirstOrDefault(x => x.Guid == item.ItemGuid);
                var editor = CreateElement<DispenserElementEditor>(item);
                InitDispenserItem(editor.DispenserItem);
            }
            else if (item is ThreadMotionItem)
            {
                var editor = CreateElement<ThreadMotionElementEditor>(item);
                InitThreadMotionItem(editor.ThreadMotionItem);
            }
            else if (item is MotorGroupItem)
            {
                var editor = CreateElement<MotorGroupElementEditor>(item);
                InitMotorGroupItem(editor.MotorGroupItem);
            }
            else if (item is DigitalOutItem)
            {
                (item as DigitalOutItem).TechIo = Adapter.TechIos.FirstOrDefault(x => x.Guid == item.ItemGuid);
                var editor = CreateElement<DigitalOutElementEditor>(item);
                InitDigitalOutItem(editor.DigitalOutItem);
            }
            else if (item is DigitalInItem)
            {
                (item as DigitalInItem).TechIo = Adapter.TechIos.FirstOrDefault(x => x.Guid == item.ItemGuid);
                CreateElement<DigitalInElementEditor>(item);
            }
            else if (item is ControllerItem)
            {
                (item as ControllerItem).TechController = Adapter.TechControllers.FirstOrDefault(x => x.Guid == item.ItemGuid);
                var editor = CreateElement<ControllerElementEditor>(item);
                InitControllerItem(editor.ControllerItem);
            }
            else if (item is PidItem)
            {
                (item as PidItem).HardwarePidType = Adapter.HardwarePidControlTypes.FirstOrDefault(x => x.Guid == item.ItemGuid);
                CreateElement<PidElementEditor>(item);
            }
            else if (item is WinderItem)
            {
                (item as WinderItem).HardwareWinderType = Adapter.HardwareWinderTypes.FirstOrDefault(x => x.Guid == item.ItemGuid);
                CreateElement<WinderElementEditor>(item);
            }
            else if (item is DancerItem)
            {
                (item as DancerItem).HardwareDancerType = Adapter.HardwareDancerTypes.FirstOrDefault(x => x.Guid == item.ItemGuid);
                CreateElement<DancerElementEditor>(item);
            }
            else if (item is SpeedSensorItem)
            {
                (item as SpeedSensorItem).HardwareSpeedSensorType = Adapter.HardwareSpeedSensorTypes.FirstOrDefault(x => x.Guid == item.ItemGuid);
                CreateElement<SpeedSensorElementEditor>(item);
            }
            else if (item is BlowerItem)
            {
                (item as BlowerItem).HardwareBlowerType = Adapter.HardwareBlowerTypes.FirstOrDefault(x => x.Guid == item.ItemGuid);
                var editor = CreateElement<BlowerElementEditor>(item);
                InitBlowerItem(editor.BlowerItem);
            }
            else if (item is BreakSensorItem)
            {
                (item as BreakSensorItem).HardwareBreakSensorType = Adapter.HardwareBreakSensorTypes.FirstOrDefault(x => x.Guid == item.ItemGuid);
                CreateElement<BreakSensorElementEditor>(item);
            }
            else if (item is ProcessParametersItem)
            {
                var editor = CreateElement<ProcessParametersElementEditor>(item);
                InitProcessParameterItem(editor.ProcessParametersItem);
            }
            else if (item is JobRunnerItem)
            {
                var editor = CreateElement<JobRunnerElementEditor>(item);
                InitJobRunnerItem(editor.JobRunnerItem);
            }
            else if (item is TextItem)
            {
                CreateElement<TextElementEditor>(item);
            }
            else if (item is MonitorRecorderItem)
            {
                var editor = CreateElement<MonitorRecorderElementEditor>(item);
                InitMonitorRecorderItem(editor.MonitorRecorderItem);
            }
        }

        /// <summary>
        /// Called when elements have been removed
        /// </summary>
        /// <param name="elements">The elements.</param>
        public void OnElementsRemoved(List<IElementEditor> elements)
        {
            foreach (var element in elements)
            {
                if (element.HostedElement is SingleGraphItem)
                {
                    var _graph_recording = _single_graphs_recordings.SingleOrDefault(x => x.Tag == element.HostedElement);

                    if (_graph_recording != null)
                    {
                        (_graph_recording.Tag as SingleGraphItem).StopRecording();
                        _single_graphs_recordings.Remove(_graph_recording);
                        _graph_recording.Dispose();
                    }
                }
                else if (element.HostedElement is MultiGraphItem)
                {
                    var _graph_recording = _multi_graph_recordings.SingleOrDefault(x => x.Tag == element.HostedElement);

                    if (_graph_recording != null)
                    {
                        (_graph_recording.Tag as MultiGraphItem).StopRecording();
                        _multi_graph_recordings.Remove(_graph_recording);
                        _graph_recording.Dispose();
                    }
                }
            }
        }

        /// <summary>
        /// Called when elements have been pasted
        /// </summary>
        /// <param name="elements">The elements.</param>
        public void OnElementsPasted(List<IElementEditor> elements)
        {
            foreach (var element in elements)
            {
                if (element is SingleGraphElementEditor)
                {
                    var graphItem = element.HostedElement as SingleGraphItem;
                    var editor = element as SingleGraphElementEditor;

                    TechGraphController controller = new TechGraphController();
                    controller.DataSeriesCollection.Add(new WpfGraphDataSeries()
                    {
                        Stroke = Colors.DodgerBlue,
                    });
                    editor.InnerGraph.Controller = controller;

                    graphItem.Editor = editor;

                    _singleControllers.Add(graphItem, controller);
                }
                else if (element is MultiGraphElementEditor)
                {
                    var graphItem = element.HostedElement as MultiGraphItem;
                    var editor = element as MultiGraphElementEditor;

                    TechGraphController controller = new TechGraphController(500);

                    for (int i = 0; i < graphItem.TechMonitor.ChannelCount; i++)
                    {
                        controller.DataSeriesCollection.Add(new WpfGraphDataSeries()
                        {
                            Stroke = ColorHelper.GetRandomColor(),
                            Name = graphItem.TechMonitor.Name.First() + (i + 1).ToString(),
                        });
                    }

                    editor.InnerGraph.Controller = controller;

                    graphItem.Editor = editor;

                    _multiControllers.Add(graphItem, controller);
                }
                else if (element is MotorElementEditor)
                {
                    var motorItem = element.HostedElement as MotorItem;
                    InitMotorItem(motorItem);
                }
                else if (element is DispenserElementEditor)
                {
                    var dispenser = element.HostedElement as DispenserItem;
                    InitDispenserItem(dispenser);
                }
                else if (element is DigitalOutElementEditor)
                {
                    var ioItem = element.HostedElement as DigitalOutItem;
                    InitDigitalOutItem(ioItem);
                }
                else if (element is ThreadMotionElementEditor)
                {
                    var threadMotionItem = element.HostedElement as ThreadMotionItem;
                    InitThreadMotionItem(threadMotionItem);
                }
                else if (element is MotorGroupElementEditor)
                {
                    var motorGroupItem = element.HostedElement as MotorGroupItem;
                    InitMotorGroupItem(motorGroupItem);
                }
                else if (element is ControllerElementEditor)
                {
                    var controllerItem = element.HostedElement as ControllerItem;
                    InitControllerItem(controllerItem);
                }
                else if (element is MonitorRecorderElementEditor)
                {
                    var item = element.HostedElement as MonitorRecorderItem;
                    InitMonitorRecorderItem(item);
                }
                else if (element is ValveElementEditor)
                {
                    var item = element.HostedElement as ValveItem;
                    InitTechValveItem(item);
                }
                else if (element is BlowerElementEditor)
                {
                    var item = element.HostedElement as BlowerItem;
                    InitBlowerItem(item);
                }
                else if (element is HeaterElementEditor)
                {
                    var item = element.HostedElement as HeaterItem;
                    InitTechHeater(item);
                }
                else if (element is ProcessParametersElementEditor)
                {
                    var item = element.HostedElement as ProcessParametersItem;
                    InitProcessParameterItem(item);
                }
            }
        }

        #endregion

        #region Init Tech Items

        /// <summary>
        /// Initializes the monitor recorder item.
        /// </summary>
        /// <param name="item">The item.</param>
        private void InitMonitorRecorderItem(MonitorRecorderItem item)
        {
            item.RecordingStarted += () =>
            {
                CommonOpenFileDialog dlg = new CommonOpenFileDialog();
                dlg.Title = "Select a folder to place all CSV files.";
                dlg.IsFolderPicker = true;
                if (dlg.ShowDialog() == CommonFileDialogResult.Ok)
                {
                    foreach (var monitor in item.GetSelectedMonitors())
                    {
                        if (!monitor.MultiChannel)
                        {
                            _single_monitors_recordings.Add(new SingleTechRecordingData(monitor.Description, dlg.FileName + "\\" + monitor.Description + ".csv") { Tag = monitor });
                        }
                        else
                        {
                            _multi_monitors_recordings.Add(new MultiTechRecordingData(monitor.Description, monitor.ChannelCount, dlg.FileName + "\\" + monitor.Description + ".csv") { Tag = monitor });
                        }
                        item.StartRecording();
                    }
                }
            };

            item.RecordingStopped += () =>
            {
                item.StopRecording();

                foreach (var sr in _single_monitors_recordings)
                {
                    sr.Dispose();
                }

                _single_monitors_recordings.Clear();

                foreach (var mr in _multi_monitors_recordings)
                {
                    mr.Dispose();
                }

                _multi_monitors_recordings.Clear();
            };
        }

        /// <summary>
        /// Initializes the tech valve.
        /// </summary>
        /// <param name="item">The valve item.</param>
        private void InitTechValveItem(ValveItem item)
        {
            item.StateChanged += async (x, state) =>
            {
                try
                {
                    CheckMachineOperator();
                    await MachineOperator.SetValveState((ValveType)item.TechValve.Code, state);
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, String.Format("Error executing technician set valve state command on '{0}'.", item.TechName));
                }
            };
        }

        /// <summary>
        /// Initializes the blower item.
        /// </summary>
        /// <param name="item">The blower item.</param>
        private void InitBlowerItem(BlowerItem item)
        {
            item.SetCommandClicked += async (_, isActive) =>
            {
                try
                {
                    CheckMachineOperator();
                    await MachineOperator.SetBlowerState((PMR.Hardware.HardwareBlowerType)item.HardwareBlower.HardwareBlowerType.Code, isActive, item.HardwareBlower.Voltage);
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, $"Error executing SetBlowerState command for blower {item.HardwareBlower.HardwareBlowerType.Name}.");
                }
            };
        }

        /// <summary>
        /// Initializes the tech heater.
        /// </summary>
        /// <param name="item">The item.</param>
        private void InitTechHeater(HeaterItem item)
        {
            item.SetCommandClicked += async () =>
            {
                try
                {
                    CheckMachineOperator();
                    await MachineOperator.SetHeaterState((HeaterType)item.TechHeater.Code, item.SetPoint);
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, $"Error executing SetHeaterState command for heater {item.TechHeater.Name}.");
                }
            };
        }

        /// <summary>
        /// Initializes the motor item.
        /// </summary>
        /// <param name="item">The item.</param>
        private void InitMotorItem(MotorItem item)
        {
            item.ActionExecuted += async (x, action) =>
            {
                try
                {
                    CheckMachineOperator();

                    if (action == MotorActionType.ForwardPressed)
                    {
                        await MachineOperator.StartMotorJogging(new MotorJoggingRequest()
                        {
                            MotorType = (PMR.Hardware.HardwareMotorType)item.HardwareMotorType.Code,
                            Speed = item.Speed,
                            Direction = MotorDirection.Forward,
                        });
                    }
                    else if (action == MotorActionType.ForwardReleased)
                    {
                        await MachineOperator.StopMotorJogging(new MotorAbortJoggingRequest()
                        {
                            MotorType = (PMR.Hardware.HardwareMotorType)item.HardwareMotorType.Code,
                        });
                    }
                    else if (action == MotorActionType.BackwardPressed)
                    {
                        await MachineOperator.StartMotorJogging(new MotorJoggingRequest()
                        {
                            MotorType = (PMR.Hardware.HardwareMotorType)item.HardwareMotorType.Code,
                            Speed = item.Speed,
                            Direction = MotorDirection.Backward,
                        });
                    }
                    else if (action == MotorActionType.BackwardReleased)
                    {
                        await MachineOperator.StopMotorJogging(new MotorAbortJoggingRequest()
                        {
                            MotorType = (PMR.Hardware.HardwareMotorType)item.HardwareMotorType.Code,
                        });
                    }
                    else if (action == MotorActionType.HomingForwardStarted || action == MotorActionType.HomingBackwardStarted)
                    {
                        item.HomingProgress = 0;
                        item.IsHoming = true;
                        item.IsHomingCompleted = false;

                        MachineOperator.StartMotorHoming(new MotorHomingRequest()
                        {
                            MotorType = (PMR.Hardware.HardwareMotorType)item.HardwareMotorType.Code,
                            Speed = item.Speed,
                            Direction = action == MotorActionType.HomingBackwardStarted ? MotorDirection.Backward : MotorDirection.Forward
                        })
                        .Subscribe((response) =>
                        {
                            item.HomingMaximumProgress = response.MaxProgress;
                            item.HomingProgress = response.Progress;

                        }, (ex) =>
                         {

                             item.IsHoming = false;
                             item.IsHomingCompleted = true;

                         }, () =>
                         {

                             item.IsHoming = false;
                             item.IsHomingCompleted = true;

                         });
                    }
                    else if (action == MotorActionType.HomingStopped)
                    {
                        await MachineOperator.StopMotorHoming(new MotorAbortHomingRequest()
                        {
                            MotorType = (PMR.Hardware.HardwareMotorType)item.HardwareMotorType.Code,
                        });

                        item.IsHoming = false;
                    }
                }
                catch (Exception ex)
                {
                    item.IsHomingCompleted = true;
                    item.IsHoming = false;
                    item.HomingProgress = 0;
                    item.HomingMaximumProgress = 0;
                    LogManager.Log(ex, String.Format("Error executing technician command '{0}' on item '{1}'.", action, item.TechName));
                }
            };
        }

        /// <summary>
        /// Initializes the dispenser item.
        /// </summary>
        /// <param name="item">The item.</param>
        private void InitDispenserItem(DispenserItem item)
        {
            item.ActionExecuted += async (x, action) =>
            {
                try
                {
                    CheckMachineOperator();

                    if (action == MotorActionType.ForwardPressed)
                    {
                        await MachineOperator.StartDispenserJogging(new DispenserJoggingRequest()
                        {
                            Index = item.TechDispenser.Code,
                            Direction = MotorDirection.Forward,
                            Speed = item.Speed,
                        });
                    }
                    else if (action == MotorActionType.ForwardReleased)
                    {
                        await MachineOperator.StopDispenserJogging(new DispenserAbortJoggingRequest()
                        {
                            Index = item.TechDispenser.Code,
                        });
                    }
                    else if (action == MotorActionType.BackwardPressed)
                    {
                        await MachineOperator.StartDispenserJogging(new DispenserJoggingRequest()
                        {
                            Index = item.TechDispenser.Code,
                            Direction = MotorDirection.Backward,
                            Speed = item.Speed,
                        });
                    }
                    else if (action == MotorActionType.BackwardReleased)
                    {
                        await MachineOperator.StopDispenserJogging(new DispenserAbortJoggingRequest()
                        {
                            Index = item.TechDispenser.Code,
                        });
                    }
                    else if (action == MotorActionType.HomingForwardStarted || action == MotorActionType.HomingBackwardStarted)
                    {
                        item.HomingProgress = 0;
                        item.IsHoming = true;
                        item.IsHomingCompleted = false;

                        MachineOperator.StartDispenserHoming(new DispenserHomingRequest()
                        {
                            Index = item.TechDispenser.Code,
                            Speed = item.Speed,
                            Direction = action == MotorActionType.HomingBackwardStarted ? MotorDirection.Backward : MotorDirection.Forward
                        })
                        .Subscribe((response) =>
                        {

                            item.HomingMaximumProgress = response.MaxProgress;
                            item.HomingProgress = response.Progress;

                        }, (ex) =>
                        {

                            item.IsHoming = false;
                            item.IsHomingCompleted = true;

                        }, () =>
                        {

                            item.IsHoming = false;
                            item.IsHomingCompleted = true;

                        });
                    }
                    else if (action == MotorActionType.HomingStopped)
                    {
                        await MachineOperator.StopDispenserHoming(new DispenserAbortHomingRequest()
                        {
                            Index = item.TechDispenser.Code,
                        });

                        item.IsHoming = false;
                    }
                }
                catch (Exception ex)
                {
                    item.IsHomingCompleted = true;
                    item.IsHoming = false;
                    item.HomingProgress = 0;
                    item.HomingMaximumProgress = 0;
                    LogManager.Log(ex, String.Format("Error executing technician command '{0}' on item '{1}'.", action, item.TechName));
                }
            };
        }

        /// <summary>
        /// Initializes the single graph item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="editor">The editor.</param>
        private void InitSingleGraphitem(SingleGraphItem item, SingleGraphElementEditor editor)
        {
            TechGraphController controller = new TechGraphController();
            controller.Range.AutoY = true;
            controller.Range.MinimumY = item.TechMonitor.Min;
            controller.Range.MaximumY = item.TechMonitor.Max;
            controller.Range.MaximumX = TimeSpan.FromSeconds(10);

            controller.DataSeriesCollection.Add(new WpfGraphDataSeries()
            {
                Stroke = Colors.DodgerBlue,
            });

            editor.InnerGraph.Controller = controller;

            item.Editor = editor;

            _singleControllers.Add(item, controller);

            item.RecordingStarted += () =>
            {
                SaveFileDialog dlg = new SaveFileDialog();
                dlg.Title = "Save graph data as csv file";
                dlg.Filter = "CSV Files|*.csv";
                dlg.DefaultExt = ".csv";
                dlg.FileName = item.TechName;
                if (dlg.ShowDialog().Value)
                {
                    _single_graphs_recordings.Add(new SingleTechRecordingData(item.TechName, dlg.FileName) { Tag = item });
                    item.StartRecording();
                }
            };

            item.RecordingStopped += () =>
            {
                try
                {
                    item.StopRecording();

                    var graph_recording = _single_graphs_recordings.SingleOrDefault(x => x.Tag == item);

                    if (graph_recording != null)
                    {
                        _single_graphs_recordings.Remove(graph_recording);
                        graph_recording.Dispose();
                    }
                }
                catch (Exception ex)
                {
                    _notification.ShowError(LogManager.Log(ex).Message);
                }
            };
        }

        /// <summary>
        /// Initializes the multi graph item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="editor">The editor.</param>
        private void InitMultiGraphItem(MultiGraphItem item, MultiGraphElementEditor editor)
        {
            TechGraphController controller = new TechGraphController(500);
            controller.Range.AutoY = true;
            controller.Range.MinimumY = item.TechMonitor.Min;
            controller.Range.MaximumY = item.TechMonitor.Max;
            controller.Range.MaximumX = TimeSpan.FromSeconds(10);

            for (int i = 0; i < item.TechMonitor.ChannelCount; i++)
            {
                controller.DataSeriesCollection.Add(new WpfGraphDataSeries()
                {
                    Stroke = ColorHelper.GetRandomColor(),
                    Name = item.TechMonitor.Name.First() + (i + 1).ToString(),
                });
            }

            editor.InnerGraph.Controller = controller;

            item.Editor = editor;

            _multiControllers.Add(item, controller);

            item.RecordingStarted += () =>
            {
                SaveFileDialog dlg = new SaveFileDialog();
                dlg.Title = "Save graph data as csv file";
                dlg.Filter = "CSV Files|*.csv";
                dlg.DefaultExt = ".csv";
                dlg.FileName = item.TechName;
                if (dlg.ShowDialog().Value)
                {
                    _multi_graph_recordings.Add(new MultiTechRecordingData(item.TechName, item.TechMonitor.ChannelCount, dlg.FileName) { Tag = item });
                    item.StartRecording();
                }
            };

            item.RecordingStopped += () =>
            {
                try
                {
                    item.StopRecording();

                    var graph_recording = _multi_graph_recordings.SingleOrDefault(x => x.Tag == item);

                    if (graph_recording != null)
                    {
                        _multi_graph_recordings.Remove(graph_recording);
                        graph_recording.Dispose();
                    }
                }
                catch (Exception ex)
                {
                    _notification.ShowError(LogManager.Log(ex).Message);
                }
            };
        }

        /// <summary>
        /// Initializes the thread motion item.
        /// </summary>
        /// <param name="item">The item.</param>
        private void InitThreadMotionItem(ThreadMotionItem item)
        {
            item.ActionExecuted += async (x, action) =>
            {
                try
                {
                    CheckMachineOperator();

                    if (action == MotorActionType.ForwardPressed)
                    {
                        await MachineOperator.StartThreadJogging(new ThreadJoggingRequest()
                        {
                            Speed = item.Speed,
                        });
                    }
                    else if (action == MotorActionType.ForwardReleased || action == MotorActionType.BackwardReleased)
                    {
                        await MachineOperator.StopThreadJogging(new ThreadAbortJoggingRequest());
                    }
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, String.Format(String.Format("Error executing technician command '{0}' on item '{1}'.", action, item.TechName)));
                }
            };
        }

        /// <summary>
        /// Initializes the motor group item.
        /// </summary>
        /// <param name="item">The item.</param>
        private void InitMotorGroupItem(MotorGroupItem item)
        {
            item.ActionExecuted += async (x, action) =>
            {
                try
                {
                    CheckMachineOperator();

                    if (action == MotorActionType.ForwardPressed)
                    {
                        await Task.WhenAll(item.TechMotors.Select(motor => MachineOperator.StartMotorJogging(new MotorJoggingRequest()
                        {
                            MotorType = (PMR.Hardware.HardwareMotorType)motor.Code,
                            Direction = MotorDirection.Forward,
                        })));
                    }
                    else if (action == MotorActionType.ForwardReleased)
                    {
                        await Task.WhenAll(item.TechMotors.Select(motor => MachineOperator.StopMotorJogging(new MotorAbortJoggingRequest()
                        {
                            MotorType = (PMR.Hardware.HardwareMotorType)motor.Code,
                        })));
                    }
                    else if (action == MotorActionType.BackwardPressed)
                    {
                        await Task.WhenAll(item.TechMotors.Select(motor => MachineOperator.StartMotorJogging(new MotorJoggingRequest()
                        {
                            MotorType = (PMR.Hardware.HardwareMotorType)motor.Code,
                            Direction = MotorDirection.Backward,
                        })));
                    }
                    else if (action == MotorActionType.BackwardReleased)
                    {
                        await Task.WhenAll(item.TechMotors.Select(motor => MachineOperator.StopMotorJogging(new MotorAbortJoggingRequest()
                        {
                            MotorType = (PMR.Hardware.HardwareMotorType)motor.Code,
                        })));
                    }
                    //else if (action == MotorActionType.HomingStarted)
                    //{
                    //    item.HomingProgress = 0;
                    //    item.IsHoming = true;
                    //    item.IsHomingCompleted = false;

                    //    MachineOperator.StartMotorHoming(new MotorHomingRequest()
                    //    {
                    //        Code = item.TechMotor.Code
                    //    })
                    //    .Subscribe((response) =>
                    //    {

                    //        item.HomingMaximumProgress = response.Message.MaxProgress;
                    //        item.HomingProgress = response.Message.Progress;

                    //    }, () =>
                    //    {

                    //        item.IsHoming = false;
                    //        item.IsHomingCompleted = true;

                    //    });
                    //}
                    //else if (action == MotorActionType.HomingStopped)
                    //{
                    //    await MachineOperator.StopMotorHoming(new MotorAbortHomingRequest()
                    //    {
                    //        Code = item.TechMotor.Code,
                    //    });

                    //    item.IsHoming = false;
                    //}
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, String.Format("Error executing technician command '{0}' on item '{1}'.", action, item.TechName));
                }
            };
        }

        /// <summary>
        /// Initializes the digital out item.
        /// </summary>
        /// <param name="item">The item.</param>
        private void InitDigitalOutItem(DigitalOutItem item)
        {
            item.ValueChanged += async (x, value) =>
            {
                try
                {
                    CheckMachineOperator();
                    await MachineOperator.SetDigitalOut(new SetDigitalOutRequest() { InterfaceIO = (InterfaceIOs)item.TechIo.Code, Value = value });
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, String.Format("Error executing technician set digital out command on '{0}'.", item.TechName));
                }
            };
        }

        /// <summary>
        /// Initializes the controller item.
        /// </summary>
        /// <param name="item">The controller item.</param>
        private void InitControllerItem(ControllerItem item)
        {
            item.SetCommandClicked += async (_, __) =>
            {
                try
                {
                    CheckMachineOperator();
                    await MachineOperator.SetComponentValue(new SetComponentValueRequest()
                    {
                        Component = (ValueComponent)item.TechController.Code,
                        Value = item.Value
                    });
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, String.Format("Error executing technician set value component command on '{0}'.", item.TechName));
                }
            };
        }

        /// <summary>
        /// Initializes the process parameter item.
        /// </summary>
        /// <param name="item">The item.</param>
        private void InitProcessParameterItem(ProcessParametersItem item)
        {
            item.PushParametersPressed += async (x, parameters) =>
            {
                try
                {
                    CheckMachineOperator();

                    using (_notification.PushTaskItem("Uploading process parameters..."))
                    {
                        try
                        {
                            await MachineOperator.UploadProcessParameters(parameters);
                        }
                        catch (Exception ex)
                        {
                            String msg = "Error uploading process parameters:" + Environment.NewLine + parameters.ToJsonString();
                            LogManager.Log(ex, msg);
                            _notification.ShowError("Could not upload process parameters." + Environment.NewLine + ex.Message);
                        }
                    }
                }
                catch (Exception ex)
                {
                    _notification.ShowError(ex.Message);
                }
            };
        }

        /// <summary>
        /// Initializes the job runner item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <exception cref="NotImplementedException"></exception>
        private void InitJobRunnerItem(JobRunnerItem item)
        {
            item.StartJob += async () =>
            {
                try
                {
                    CheckMachineOperator();

                    var handler = await MachineOperator.Print(item.Job, item.ProcessParameters);

                    item.JobHandler = handler;

                    handler.StatusChanged += (x, status) =>
                    {
                        item.RunningJobStatus = status;
                    };

                    handler.Stopped += (x, e) =>
                    {
                        item.IsJobStarted = false;
                    };
                }
                catch (Exception ex)
                {
                    item.IsJobStarted = false;
                    _notification.ShowError(ex.Message);
                }
            };

            item.StopJob += () =>
            {
                if (item.JobHandler != null)
                {
                    item.JobHandler.Cancel();
                    item.JobHandler = null;
                }
            };
        }

        /// <summary>
        /// Checks the machine operator.
        /// </summary>
        /// <exception cref="InvalidOperationException">No machine connected.</exception>
        private void CheckMachineOperator()
        {
            if (MachineOperator == null || MachineOperator.State != Transport.TransportComponentState.Connected)
            {
                throw new InvalidOperationException("No machine connected.");
            }
        }

        #endregion

        #region Project Management

        /// <summary>
        /// Opens a file open dialog to select a project file.
        /// </summary>
        public void OpenProject()
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Title = "Select Technician Project File";
            dlg.Filter = "Technician Project File|*.tpf";

            if (dlg.ShowDialog().Value)
            {
                OpenProjectFile(dlg.FileName);
            }
        }

        /// <summary>
        /// Opens the specified project file path.
        /// </summary>
        /// <param name="fileName">File path.</param>
        public MachineTechViewProject OpenProjectFile(String fileName, bool load = true)
        {
            MachineTechViewProject project = null;

            try
            {
                project = MachineTechViewProject.Load(fileName);

                if (project.Tabs.Count == 0)
                {
                    LogManager.Log($"Error loading project file {fileName}. Trying to load using legacy project loader.", LogCategory.Warning);

                    MachineTechViewProjectTab tab = new MachineTechViewProjectTab();
                    tab.Name = "Untitled";
                    tab.Items.AddRange(project.Items);
                    project.Items.Clear();
                    project.Tabs.Add(tab);
                }

                if (load)
                {
                    LoadProject(project);
                    _lastTechProjectFile = fileName;
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, $"Error loading project file {fileName}.");
                _notification.ShowError("An error occurred while trying to load the tech board project file.");
            }

            return project;
        }

        /// <summary>
        /// Loads the specified project.
        /// </summary>
        /// <param name="project">The project.</param>
        public void LoadProject(MachineTechViewProject project, bool appendToCurrentProject = false)
        {
            using (_notification.PushTaskItem("Loading technician project file..."))
            {
                if (!appendToCurrentProject)
                {
                    Tabs.Clear();
                    _singleControllers.Clear();
                    _multiControllers.Clear();
                }

                foreach (var tab in project.Tabs)
                {
                    MachineTechTabVM t = new MachineTechTabVM();
                    t.Name = tab.Name;
                    Tabs.Add(t);

                    SelectedTab = t;

                    foreach (var item in tab.Items)
                    {
                        if (item is MotorGroupItem)
                        {
                            (item as MotorGroupItem).TechMotors = ObservablesStaticCollections.Instance.HardwareMotorTypes.Where(x => (item as MotorGroupItem).ItemsGuids.Contains(x.Guid)).ToObservableCollection();
                        }
                        else if (item is MonitorRecorderItem)
                        {
                            (item as MonitorRecorderItem).SetSelectedMonitors(ObservablesStaticCollections.Instance.TechMonitors.Where(x => (item as MonitorRecorderItem).SelectedMonitorsGuids.Contains(x.Guid)).ToList());
                        }

                        AddTechItem(item);
                    }
                }

                SelectedTab = Tabs.ElementAt(project.SelectedTabIndex);
            }
        }

        /// <summary>
        /// Opens the file save dialog for selecting a project file target.
        /// </summary>
        private void SaveAsProject()
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Title = "Select Technician Project Location";
            dlg.Filter = "Technician Project File|*.tpf";

            if (dlg.ShowDialog().Value)
            {
                SaveProjectFile(dlg.FileName);
            }
        }

        /// <summary>
        /// Saves the current project to the specified file path.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        private void SaveProjectFile(String fileName)
        {
            using (_notification.PushTaskItem("Saving technician project file..."))
            {
                MachineTechViewProject project = GenerateProjectFile();
                project.Save(fileName);
                _lastTechProjectFile = fileName;
            }
        }

        /// <summary>
        /// Saves the current opened project file. If not project file is opened will call <see cref="SaveAsProject"/>.
        /// </summary>
        private void SaveProject()
        {
            if (File.Exists(_lastTechProjectFile))
            {
                SaveProjectFile(_lastTechProjectFile);
            }
            else
            {
                SaveAsProject();
            }
        }

        /// <summary>
        /// Generates a project file from the current element setup.
        /// </summary>
        /// <returns></returns>
        private MachineTechViewProject GenerateProjectFile()
        {
            MachineTechViewProject project = new MachineTechViewProject();
            project.SelectedTabIndex = Tabs.IndexOf(SelectedTab);

            foreach (var tab in Tabs)
            {
                MachineTechViewProjectTab pTab = new MachineTechViewProjectTab();
                pTab.Name = tab.Name;

                foreach (var element in tab.Elements)
                {
                    if (element.HostedElement is MotorGroupItem)
                    {
                        var group = element.HostedElement as MotorGroupItem;
                        group.ItemsGuids = group.TechMotors.Select(x => x.Guid).ToList();
                    }

                    (element.HostedElement as TechItem).SetBounds(element.GetBounds());
                    pTab.Items.Add(element.HostedElement as TechItem);
                }

                project.Tabs.Add(pTab);
            }

            return project;
        }

        /// <summary>
        /// Removes the specified tab.
        /// </summary>
        /// <param name="tab">The tab.</param>
        private void RemoveTab(MachineTechTabVM tab)
        {
            if (_notification.ShowQuestion("Are you sure you want to delete the selected tab?"))
            {
                Tabs.Remove(tab);
                SelectedTab = Tabs.LastOrDefault();

                if (SelectedTab == null)
                {
                    AddNewTab("Untitled");
                }
            }
        }

        /// <summary>
        /// Adds a new tab.
        /// </summary>
        private bool AddNewTab(String name = null)
        {
            if (Tabs.Count > 7)
            {
                _notification.ShowError("Cannot exceed the maximum number of 8 tabs. You can remove a tab, or create a new project.");
                return false;
            }

            if (name == null)
            {
                name = _notification.ShowTextInput("Enter tab name", "Tab Name", "Untitled");
            }

            if (!String.IsNullOrWhiteSpace(name))
            {
                MachineTechTabVM t = new MachineTechTabVM();
                t.Name = name;
                Tabs.Add(t);
                SelectedTab = t;
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Creates a new project.
        /// </summary>
        private void CreateNewProject()
        {
            var to_remove = Tabs.ToList();

            var name = _notification.ShowTextInput("Enter project name", "Project Name", "untitled");

            if (String.IsNullOrWhiteSpace(name))
            {
                return;
            }

            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Title = "Select Technician Project Location";
            dlg.Filter = "Technician Project File|*.tpf";
            dlg.DefaultExt = ".tpf";
            dlg.FileName = name;

            if (!dlg.ShowDialog().Value)
            {
                return;
            }

            _singleControllers.Clear();
            _multiControllers.Clear();
            _multi_graph_recordings.Clear();
            _multi_monitors_recordings.Clear();
            _single_graphs_recordings.Clear();
            _single_monitors_recordings.Clear();

            foreach (var tab in to_remove)
            {
                Tabs.Remove(tab);
            }

            AddNewTab(name);

            _lastTechProjectFile = dlg.FileName;
            File.AppendAllText(_lastTechProjectFile, "");
            SaveProject();
        }

        /// <summary>
        /// Renames the tab.
        /// </summary>
        /// <param name="tab">The tab.</param>
        private void RenameTab()
        {
            if (SelectedTab != null)
            {
                var name = _notification.ShowTextInput("Enter tab name", "Tab Name", SelectedTab.Name);

                if (!String.IsNullOrWhiteSpace(name))
                {
                    SelectedTab.Name = name;
                }
            }
        }

        /// <summary>
        /// Imports a project file tabs to the current project.
        /// </summary>
        private void ImportProjectTabs()
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Title = "Select Technician Project File";
            dlg.Filter = "Technician Project File|*.tpf";

            if (dlg.ShowDialog().Value)
            {
                var project = OpenProjectFile(dlg.FileName, false);

                if (project != null)
                {
                    var vm = new ImportProjectTabViewVM(project.Tabs, Path.GetFileNameWithoutExtension(dlg.FileName));
                    _notification.ShowModalDialog<ImportProjectTabViewVM, ImportProjectTabView>(vm, (x) =>
                    {
                        project.Tabs.Clear();
                        project.Tabs.AddRange(vm.Tabs.SynchedSource.ToList());
                        LoadProject(project, true);

                    }, () => { });
                }
            }
        }

        #endregion

        #region IStudioModuleVM

        public override void OnNavigatedTo()
        {
            base.OnNavigatedTo();
            //_singleControllers.ToList().ForEach(x => x.Value.ChangeRenderMode(true));
        }

        public override void OnNavigatedFrom()
        {
            base.OnNavigatedFrom();
            //_singleControllers.ToList().ForEach(x => x.Value.ChangeRenderMode(false));
        }

        public override void OnShuttingDown()
        {
            InvokeUINow(() =>
            {
                _settings.LastTechProjectFile = _lastTechProjectFile;
            });
        }

        public override void OnApplicationReady()
        {
            if (_populateFramesThread == null)
            {
                _populateFramesThread = new Thread(PopulateFramesThreadMethod);
                _populateFramesThread.IsBackground = true;
                _populateFramesThread.Start();
            }
        }

        #endregion

        #region Hardware Configuration

        private void SyncHardwareConfiguration()
        {
            var elements = Tabs.SelectMany(x => x.Elements).ToList();

            if (MachineOperator != null && MachineOperator.CurrentHardwareConfiguration != null)
            {
                var config = MachineOperator.CurrentHardwareConfiguration;

                foreach (var motorConfig in config.Motors)
                {
                    var itemConfig = MotorItem.MotorConfigurations.SingleOrDefault(x => x.HardwareMotorType.Code == motorConfig.HardwareMotorType.ToInt32());

                    if (itemConfig != null)
                    {
                        motorConfig.MapPrimitivesTo(itemConfig);
                    }
                }

                foreach (var pidConfig in config.PidControls)
                {
                    var itemConfig = PidItem.PidConfigurations.SingleOrDefault(x => x.HardwarePidControlType.Code == pidConfig.HardwarePidControlType.ToInt32());

                    if (itemConfig != null)
                    {
                        pidConfig.MapPrimitivesTo(itemConfig);
                    }
                }

                foreach (var winderConfig in config.Winders)
                {
                    var itemConfig = WinderItem.WinderConfigurations.SingleOrDefault(x => x.HardwareWinderType.Code == winderConfig.HardwareWinderType.ToInt32());

                    if (itemConfig != null)
                    {
                        winderConfig.MapPrimitivesTo(itemConfig);
                    }
                }

                foreach (var dancerConfig in config.Dancers)
                {
                    var itemConfig = DancerItem.DancerConfigurations.SingleOrDefault(x => x.HardwareDancerType.Code == dancerConfig.HardwareDancerType.ToInt32());

                    if (itemConfig != null)
                    {
                        dancerConfig.MapPrimitivesTo(itemConfig);
                    }
                }

                foreach (var speedSensorConfig in config.SpeedSensors)
                {
                    var itemConfig = SpeedSensorItem.SpeedSensorConfigurations.SingleOrDefault(x => x.HardwareSpeedSensorType.Code == speedSensorConfig.HardwareSpeedSensorType.ToInt32());

                    if (itemConfig != null)
                    {
                        speedSensorConfig.MapPrimitivesTo(itemConfig);
                    }
                }

                foreach (var blowerConfig in config.Blowers)
                {
                    var itemConfig = BlowerItem.BlowerConfigurations.SingleOrDefault(x => x.HardwareBlowerType.Code == blowerConfig.HardwareBlowerType.ToInt32());

                    if (itemConfig != null)
                    {
                        blowerConfig.MapPrimitivesTo(itemConfig);
                    }
                }

                foreach (var breakSensorConfig in config.BreakSensors)
                {
                    var itemConfig = BreakSensorItem.BreakSensorConfigurations.SingleOrDefault(x => x.HardwareBreakSensorType.Code == breakSensorConfig.HardwareBreakSensorType.ToInt32());

                    if (itemConfig != null)
                    {
                        breakSensorConfig.MapPrimitivesTo(itemConfig);
                    }
                }

                _notification.ShowInfo("Visual elements synced to the last uploaded hardware configuration.");
            }
            else
            {
                ResetHardwareConfiguration();
            }
        }

        private async void UploadHardwareConfiguration()
        {
            if (MachineOperator != null)
            {
                using (_notification.PushTaskItem("Uploading hardware configuration..."))
                {
                    try
                    {
                        HardwareVersion hw = null;
                        Configuration config = null;

                        await Task.Factory.StartNew(() =>
                        {
                            using (ObservablesContext db = ObservablesContext.CreateDefault())
                            {
                                config = db.Adapter.GetConfiguration(x => x.Guid == ApplicationManager.Machine.ConfigurationGuid);
                                hw = db.Adapter.GetHardwareVersionByMachine(ApplicationManager.Machine.Guid).Clone();
                            }
                        });

                        foreach (var motorConfig in hw.HardwareMotors)
                        {
                            var itemConfig = MotorItem.MotorConfigurations.SingleOrDefault(x => x.HardwareMotorType.Code == motorConfig.HardwareMotorType.Code);

                            if (itemConfig != null)
                            {
                                itemConfig.MapPrimitivesTo(motorConfig);
                            }
                        }

                        foreach (var pidConfig in hw.HardwarePidControls)
                        {
                            var itemConfig = PidItem.PidConfigurations.SingleOrDefault(x => x.HardwarePidControlType.Code == pidConfig.HardwarePidControlType.Code);

                            if (itemConfig != null)
                            {
                                itemConfig.MapPrimitivesTo(pidConfig);
                            }
                        }

                        foreach (var winderConfig in hw.HardwareWinders)
                        {
                            var itemConfig = WinderItem.WinderConfigurations.SingleOrDefault(x => x.HardwareWinderType.Code == winderConfig.HardwareWinderType.Code);

                            if (itemConfig != null)
                            {
                                itemConfig.MapPrimitivesTo(winderConfig);
                            }
                        }

                        foreach (var dancerConfig in hw.HardwareDancers)
                        {
                            var itemConfig = DancerItem.DancerConfigurations.SingleOrDefault(x => x.HardwareDancerType.Code == dancerConfig.HardwareDancerType.Code);

                            if (itemConfig != null)
                            {
                                itemConfig.MapPrimitivesTo(dancerConfig);
                            }
                        }

                        foreach (var speedSensorConfig in hw.HardwareSpeedSensors)
                        {
                            var itemConfig = SpeedSensorItem.SpeedSensorConfigurations.SingleOrDefault(x => x.HardwareSpeedSensorType.Code == speedSensorConfig.HardwareSpeedSensorType.Code);

                            if (itemConfig != null)
                            {
                                itemConfig.MapPrimitivesTo(speedSensorConfig);
                            }
                        }

                        foreach (var blowerConfig in hw.HardwareBlowers)
                        {
                            var itemConfig = BlowerItem.BlowerConfigurations.SingleOrDefault(x => x.HardwareBlowerType.Code == blowerConfig.HardwareBlowerType.Code);

                            if (itemConfig != null)
                            {
                                itemConfig.MapPrimitivesTo(blowerConfig);
                            }
                        }

                        foreach (var breakSensorConfig in hw.HardwareBreakSensors)
                        {
                            var itemConfig = BreakSensorItem.BreakSensorConfigurations.SingleOrDefault(x => x.HardwareBreakSensorType.Code == breakSensorConfig.HardwareBreakSensorType.Code);

                            if (itemConfig != null)
                            {
                                itemConfig.MapPrimitivesTo(breakSensorConfig);
                            }
                        }

                        await MachineOperator.UploadHardwareConfiguration(hw, config);
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, "Error uploading hardware configuration.");
                        _notification.ShowError("An error occurred while trying to upload the hardware configuration." + Environment.NewLine + ex.Message);
                    }
                }
            }
        }

        private async void UploadPartialHardwareConfiguration()
        {
            if (MachineOperator != null)
            {
                if (_notification.ShowQuestion("This will upload only visible elements across all tabs as a hardware configuration package. Are you sure?"))
                {
                    using (_notification.PushTaskItem("Uploading partial hardware configuration..."))
                    {
                        try
                        {
                            HardwareVersion hw = new HardwareVersion();
                            hw.Name = "Partial HW Configuration";
                            hw.Version = 1;

                            Configuration config = null;

                            foreach (var element in Tabs.SelectMany(x => x.Elements).ToList())
                            {
                                var item = element.HostedElement as TechItem;

                                if (item is MotorItem)
                                {
                                    hw.HardwareMotors.Add((item as MotorItem).HardwareMotor);
                                }
                                else if (item is DancerItem)
                                {
                                    hw.HardwareDancers.Add((item as DancerItem).HardwareDancer);
                                }
                                else if (item is PidItem)
                                {
                                    hw.HardwarePidControls.Add((item as PidItem).HardwarePid);
                                }
                                else if (item is BlowerItem)
                                {
                                    hw.HardwareBlowers.Add((item as BlowerItem).HardwareBlower);
                                }
                                else if (item is BreakSensorItem)
                                {
                                    hw.HardwareBreakSensors.Add((item as BreakSensorItem).HardwareBreakSensor);
                                }
                                else if (item is SpeedSensorItem)
                                {
                                    hw.HardwareSpeedSensors.Add((item as SpeedSensorItem).HardwareSpeedSensor);
                                }
                                else if (item is WinderItem)
                                {
                                    hw.HardwareWinders.Add((item as WinderItem).HardwareWinder);
                                }
                            }

                            await Task.Factory.StartNew(() =>
                            {
                                using (ObservablesContext db = ObservablesContext.CreateDefault())
                                {
                                    config = db.Adapter.GetConfiguration(x => x.Guid == ApplicationManager.Machine.ConfigurationGuid).Clone();
                                }
                            });

                            await MachineOperator.UploadHardwareConfiguration(hw, config);
                        }
                        catch (Exception ex)
                        {
                            LogManager.Log(ex, "Error uploading partial hardware configuration.");
                            _notification.ShowError("An error occurred while trying to upload the partial hardware configuration." + Environment.NewLine + ex.Message);
                        }
                    }
                }
            }
        }

        private async void ResetHardwareConfiguration(bool showMessage = true)
        {
            if (MachineOperator != null)
            {
                ObservablesContext db = ObservablesContext.CreateDefault();
                ObservablesContextAdapter adapter = new ObservablesContextAdapter(db);

                HardwareVersion hw = null;

                await Task.Factory.StartNew(() =>
                {
                    hw = adapter.GetHardwareVersionByMachine(ApplicationManager.Machine.Guid);
                });

                foreach (var motorConfig in hw.HardwareMotors)
                {
                    var itemConfig = MotorItem.MotorConfigurations.SingleOrDefault(x => x.HardwareMotorType.Code == motorConfig.HardwareMotorType.Code);

                    if (itemConfig != null)
                    {
                        motorConfig.MapPrimitivesTo(itemConfig);
                    }
                }

                foreach (var pidConfig in hw.HardwarePidControls)
                {
                    var itemConfig = PidItem.PidConfigurations.SingleOrDefault(x => x.HardwarePidControlType.Code == pidConfig.HardwarePidControlType.Code);

                    if (itemConfig != null)
                    {
                        pidConfig.MapPrimitivesTo(itemConfig);
                    }
                }

                foreach (var winderConfig in hw.HardwareWinders)
                {
                    var itemConfig = WinderItem.WinderConfigurations.SingleOrDefault(x => x.HardwareWinderType.Code == winderConfig.HardwareWinderType.Code);

                    if (itemConfig != null)
                    {
                        winderConfig.MapPrimitivesTo(itemConfig);
                    }
                }

                foreach (var dancerConfig in hw.HardwareDancers)
                {
                    var itemConfig = DancerItem.DancerConfigurations.SingleOrDefault(x => x.HardwareDancerType.Code == dancerConfig.HardwareDancerType.Code);

                    if (itemConfig != null)
                    {
                        dancerConfig.MapPrimitivesTo(itemConfig);
                    }
                }

                foreach (var speedSensorConfig in hw.HardwareSpeedSensors)
                {
                    var itemConfig = SpeedSensorItem.SpeedSensorConfigurations.SingleOrDefault(x => x.HardwareSpeedSensorType.Code == speedSensorConfig.HardwareSpeedSensorType.Code);

                    if (itemConfig != null)
                    {
                        speedSensorConfig.MapPrimitivesTo(itemConfig);
                    }
                }

                foreach (var blowerSensorConfig in hw.HardwareBlowers)
                {
                    var itemConfig = BlowerItem.BlowerConfigurations.SingleOrDefault(x => x.HardwareBlowerType.Code == blowerSensorConfig.HardwareBlowerType.Code);

                    if (itemConfig != null)
                    {
                        blowerSensorConfig.MapPrimitivesTo(itemConfig);
                    }
                }

                foreach (var breakSensorConfig in hw.HardwareBreakSensors)
                {
                    var itemConfig = BreakSensorItem.BreakSensorConfigurations.SingleOrDefault(x => x.HardwareBreakSensorType.Code == breakSensorConfig.HardwareBreakSensorType.Code);

                    if (itemConfig != null)
                    {
                        breakSensorConfig.MapPrimitivesTo(itemConfig);
                    }
                }

                if (showMessage)
                {
                    _notification.ShowInfo("Visual elements synced to hardware version " + hw.Name + ", " + hw.Version + ".");
                }
            }
        }

        #endregion

        #region Graphs

        public void ClearAllGraphs()
        {
            _start_time = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0);
            _last_time = DateTime.Now;

            foreach (var controller in _singleControllers)
            {
                controller.Value.Clear();
            }

            foreach (var controller in _multiControllers)
            {
                controller.Value.Clear();
            }
        }

        #endregion
    }
}