aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Developer/Images/seamless-grid.jpg
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Developer/Images/seamless-grid.jpg')
0 files changed, 0 insertions, 0 deletions
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;

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;

        #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)
        {
            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)
        {
            PopulateDiagnosticsData(response);
        }

        #endregion

        #region Populate Diagnostics Data

        /// <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()
        {

        }

        #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
    }
}