using GalaSoft.MvvmLight.Ioc; using Google.Protobuf.Collections; using Microsoft.Win32; using RealTimeGraphEx.Controllers; 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.Integration.Observables; using Tango.Integration.Observables.Enumerations; using Tango.Integration.Operators; 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; namespace Tango.MachineStudio.Technician.ViewModels { public class MachineTechViewVM : ViewModel, IShutdownListener { private List _diagnoticsDataProperties; private Dictionary _singleControllers; private Dictionary _multiControllers; private static object _elementsLock = new object(); private String _lastTechProjectFile; private INotificationProvider _notification; #region Properties private ObservableCollection _elements; /// /// Gets or sets the visual elements. /// public ObservableCollection Elements { get { return _elements; } set { _elements = value; RaisePropertyChangedAuto(); } } private ObservableCollection _availableTechItems; /// /// Gets or sets the available tech items. /// public ObservableCollection AvailableTechItems { get { return _availableTechItems; } set { _availableTechItems = value; RaisePropertyChangedAuto(); } } private TechItem _selectedTechItem; /// /// Gets or sets the selected available tech item. /// public TechItem SelectedTechItem { get { return _selectedTechItem; } set { _selectedTechItem = value; RaisePropertyChangedAuto(); } } /// /// Gets or sets the db adapter. /// public ObservablesEntitiesAdapter Adapter { get; set; } /// /// Gets or sets the application manager. /// public IStudioApplicationManager ApplicationManager { get; set; } private IMachineOperator _machineOperator; /// /// Gets or sets the machine operator. /// public IMachineOperator MachineOperator { get { return _machineOperator; } set { _machineOperator = value; RaisePropertyChangedAuto(); } } private bool _disableRendering; /// /// Gets or sets a value indicating whether [disable rendering]. /// public bool DisableRendering { get { return _disableRendering; } set { _disableRendering = value; RaisePropertyChangedAuto(); OnDisableRenderingChanged(); } } private bool _hideMenu; /// /// Gets or sets a value indicating whether [hide menu]. /// public bool HideMenu { get { return _hideMenu; } set { _hideMenu = value; RaisePropertyChangedAuto(); } } #endregion #region Commands public RelayCommand SaveAsProjectCommand { get; set; } public RelayCommand SaveProjectCommand { get; set; } public RelayCommand OpenProjectCommand { get; set; } #endregion #region Constructors [PreferredConstructor] public MachineTechViewVM(IStudioApplicationManager applicationManager, INotificationProvider notificationProvider) : this(applicationManager, notificationProvider, true) { } public MachineTechViewVM(IStudioApplicationManager applicationManager, INotificationProvider notificationProvider, bool loadLastProject) { _notification = notificationProvider; _singleControllers = new Dictionary(); _multiControllers = new Dictionary(); AvailableTechItems = TechItem.GetAvailableTechItems().ToObservableCollection(); SelectedTechItem = AvailableTechItems.FirstOrDefault(); _diagnoticsDataProperties = typeof(PushDiagnosticsResponse).GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList(); ApplicationManager = applicationManager; ApplicationManager.ConnectedMachineChanged += ApplicationManager_ConnectedMachineChanged; Adapter = ObservablesEntitiesAdapter.Instance; Elements = new ObservableCollection(); OpenProjectCommand = new RelayCommand(OpenProject); SaveAsProjectCommand = new RelayCommand(SaveAsProject); SaveProjectCommand = new RelayCommand(SaveProject); _lastTechProjectFile = SettingsManager.Default.MachineStudio.TechnicianModule.LasTechProjectFile; if (File.Exists(_lastTechProjectFile) && loadLastProject) { OpenProjectFile(_lastTechProjectFile); } } #endregion #region Event Handlers /// /// Applications the manager connected machine changed. /// /// The sender. /// The machine. private void ApplicationManager_ConnectedMachineChanged(object sender, Integration.Services.IExternalBridgeClient machine) { MachineOperator = machine; if (MachineOperator != null) { MachineOperator.DiagnosticsDataAvailable -= MachineOperator_DiagnosticsDataAvailable; MachineOperator.DiagnosticsDataAvailable += MachineOperator_DiagnosticsDataAvailable; } } /// /// Machines the operator diagnostics data available. /// /// The sender. /// The response. private void MachineOperator_DiagnosticsDataAvailable(object sender, PushDiagnosticsResponse response) { PopulateDiagnosticsData(response); } #endregion #region Populate Diagnostics Data private void PopulateDiagnosticsData(PushDiagnosticsResponse data) { lock (_elementsLock) { var elements = Elements.ToList(); foreach (var item in elements.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 = _diagnoticsDataProperties.SingleOrDefault(x => x.Name == monitorItem.TechMonitor.Name); if (prop != null) { monitorItem.Value = GetLastMonitorValue(monitorItem.TechMonitor, prop.GetValue(data)); } } } else if (item.GetType() == typeof(DigitalOutItem)) { DigitalOutItem digitalOutItem = item as DigitalOutItem; var digitalPin = data.DigitalPins.SingleOrDefault(x => x.Port == digitalOutItem.TechIo.Port); if (digitalPin != null) { digitalOutItem.EffectiveValue = digitalPin.Value; } } else if (item.GetType() == typeof(MeterItem)) { MeterItem meterItem = item as MeterItem; if (DateTime.Now > meterItem.LastUpdateTime.AddMilliseconds(meterItem.UpdateInterval)) { var prop = _diagnoticsDataProperties.SingleOrDefault(x => x.Name == meterItem.TechMonitor.Name); if (prop != null) { meterItem.Value = GetLastMonitorValue(meterItem.TechMonitor, prop.GetValue(data)); } } } else if (item.GetType() == typeof(SingleGraphItem)) { SingleGraphItem graphItem = item as SingleGraphItem; var prop = _diagnoticsDataProperties.SingleOrDefault(x => x.Name == graphItem.TechMonitor.Name); if (prop != null) { GraphController controller = null; if (_singleControllers.TryGetValue(graphItem, out controller)) { controller.PushData(GetSingleGraphValues(graphItem.TechMonitor, prop.GetValue(data))); } } } else if (item.GetType() == typeof(MultiGraphItem)) { MultiGraphItem graphItem = item as MultiGraphItem; var prop = _diagnoticsDataProperties.SingleOrDefault(x => x.Name == graphItem.TechMonitor.Name); if (prop != null) { GraphMultiController controller = null; if (_multiControllers.TryGetValue(graphItem, out controller)) { controller.PushData(GetMultiGraphValues(graphItem.TechMonitor, prop.GetValue(data))); } } } } } } #endregion #region Private Methods private double GetLastMonitorValue(TechMonitor monitor, object value) { if (!monitor.MultiChannel) { RepeatedField arr = value as RepeatedField; return arr.LastOrDefault(); } else { RepeatedField arr = value as RepeatedField; return arr.Last().Data.Last(); } } private List GetSingleGraphValues(TechMonitor monitor, object value) { return (value as RepeatedField).ToList(); } private List> GetMultiGraphValues(TechMonitor monitor, object value) { DoubleArray[] arrayOfDoubles = Enumerable.ToArray(value as IEnumerable); return arrayOfDoubles.Select(x => x.Data.ToList()).ToList(); } #endregion #region Virtual Methods 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 public void CreateElement(Rect bounds) { CreateElement(SelectedTechItem, bounds); } private void CreateElement(TechItem item, Rect bounds) { if (item is MonitorItem) { CreateElement(bounds, Adapter.TechMonitors.Where(x => !x.MultiChannel).FirstOrDefault()); } else if (item is MeterItem) { CreateElement(bounds, Adapter.TechMonitors.Where(x => !x.MultiChannel).FirstOrDefault()); } else if (item is SingleGraphItem) { var editor = CreateElement(bounds, Adapter.TechMonitors.Where(x => !x.MultiChannel).FirstOrDefault()); InitSingleGraphitem(editor.GraphItem, editor); } else if (item is MultiGraphItem) { var editor = CreateElement(bounds, Adapter.TechMonitors.Where(x => x.MultiChannel).FirstOrDefault()); InitMultiGraphItem(editor.GraphItem, editor); } else if (item is MotorItem) { var editor = CreateElement(bounds, Adapter.TechMotors.FirstOrDefault()); InitMotorItem(editor.MotorItem); } else if (item is DispenserItem) { var editor = CreateElement(bounds, Adapter.TechDispensers.FirstOrDefault()); InitDispenserItem(editor.DispenserItem); } else if (item is DigitalOutItem) { var editor = CreateElement(bounds, Adapter.TechIos.Where(x => x.Type == IOType.DigitalOutput.ToInt32()).FirstOrDefault()); InitDigitalOutItem(editor.DigitalOutItem); } else if (item is ThreadMotionItem) { var editor = CreateElement(bounds, null); InitThreadMotionItem(editor.ThreadMotionItem); } else if (item is MotorGroupItem) { var editor = CreateElement(bounds, null); InitMotorGroupItem(editor.MotorGroupItem); } } private Editor CreateElement(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; Elements.Add(editor); return (Editor)editor; } private Editor CreateElement(TechItem item) where Editor : IElementEditor { IElementEditor editor = Activator.CreateInstance(typeof(Editor), new object[] { item, item.GetBounds() }) as IElementEditor; Elements.Add(editor); return (Editor)editor; } 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(item); } else if (item is MeterItem) { (item as MeterItem).TechMonitor = Adapter.TechMonitors.Where(x => !x.MultiChannel).FirstOrDefault(x => x.Guid == item.ItemGuid); CreateElement(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(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(item); InitMultiGraphItem(editor.GraphItem, editor); } else if (item is MotorItem) { (item as MotorItem).TechMotor = Adapter.TechMotors.FirstOrDefault(x => x.Guid == item.ItemGuid); var editor = CreateElement(item); InitMotorItem(editor.MotorItem); } else if (item is DispenserItem) { (item as DispenserItem).TechDispenser = Adapter.TechDispensers.FirstOrDefault(x => x.Guid == item.ItemGuid); var editor = CreateElement(item); InitDispenserItem(editor.DispenserItem); } else if (item is DigitalOutItem) { var editor = CreateElement(item); InitDigitalOutItem(editor.DigitalOutItem); } else if (item is ThreadMotionItem) { var editor = CreateElement(item); InitThreadMotionItem(editor.ThreadMotionItem); } else if (item is MotorGroupItem) { var editor = CreateElement(item); InitMotorGroupItem(editor.MotorGroupItem); } } public void OnElementsRemoved(List elements) { //foreach (var element in elements) //{ // if (element.HostedElement is SingleGraphItem) // { // _singleControllers.Remove(element.HostedElement as SingleGraphItem); // (element.HostedElement as SingleGraphItem).Editor.InnerGraph.InnerGraph.Dispose(); // } // else if (element.HostedElement is MultiGraphItem) // { // _multiControllers.Remove(element.HostedElement as MultiGraphItem); // (element.HostedElement as MultiGraphItem).Editor.InnerGraph.InnerGraph.Dispose(); // } //} } public void OnElementsPasted(List elements) { foreach (var element in elements) { if (element is SingleGraphElementEditor) { var graphItem = element.HostedElement as SingleGraphItem; var editor = element as SingleGraphElementEditor; graphItem.Editor = editor; editor.InnerGraph.InnerGraph.MaxPoints = Common.Helpers.GraphsHelper.GetMaxPoints(graphItem.TechMonitor.PointsPerFrame); GraphController controller = new GraphController(); editor.InnerGraph.Controller = controller; _singleControllers.Add(graphItem, controller); } else if (element is MultiGraphElementEditor) { var graphItem = element.HostedElement as MultiGraphItem; var editor = element as MultiGraphElementEditor; editor.InnerGraph.InnerGraph.MaxPoints = Common.Helpers.GraphsHelper.GetMaxPoints(graphItem.TechMonitor.PointsPerFrame); graphItem.Editor = editor; GraphMultiController controller = new GraphMultiController(); for (int i = 0; i < graphItem.TechMonitor.ChannelCount; i++) { controller.AddSeries(new RealTimeGraphEx.DataSeries.DataYSeries() { UseFillAndStroke = true, Name = graphItem.TechMonitor.Name.First() + (i + 1).ToString(), Stroke = new SolidColorBrush(ColorHelper.GetRandomColor()), }); } editor.InnerGraph.Controller = controller; _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 DigitalOutItem) { var ioItem = element.HostedElement as DigitalOutItem; InitDigitalOutItem(ioItem); } else if (element is ThreadMotionItem) { var threadMotionItem = element.HostedElement as ThreadMotionItem; InitThreadMotionItem(threadMotionItem); } else if (element is MotorGroupItem) { var motorGroupItem = element.HostedElement as MotorGroupItem; InitMotorGroupItem(motorGroupItem); } } } #endregion #region Init Tech Items private void InitMotorItem(MotorItem item) { item.ActionExecuted += async (x, action) => { if (action == MotorActionType.ForwardPressed) { await MachineOperator.StartMotorJogging(new MotorJoggingRequest() { Code = item.TechMotor.Code, Direction = MotorDirection.Forward, }); } else if (action == MotorActionType.ForwardReleased) { await MachineOperator.StopMotorJogging(new MotorAbortJoggingRequest() { Code = item.TechMotor.Code, }); } else if (action == MotorActionType.BackwardPressed) { await MachineOperator.StartMotorJogging(new MotorJoggingRequest() { Code = item.TechMotor.Code, Direction = MotorDirection.Backward, }); } else if (action == MotorActionType.BackwardReleased) { await MachineOperator.StopMotorJogging(new MotorAbortJoggingRequest() { Code = item.TechMotor.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; } }; } private void InitDispenserItem(DispenserItem item) { item.ActionExecuted += async (x, action) => { if (action == MotorActionType.ForwardPressed) { await MachineOperator.StartDispenserJogging(new DispenserJoggingRequest() { Code = item.TechDispenser.Code, Direction = MotorDirection.Forward, }); } else if (action == MotorActionType.ForwardReleased) { await MachineOperator.StopDispenserJogging(new DispenserAbortJoggingRequest() { Code = item.TechDispenser.Code, }); } else if (action == MotorActionType.BackwardPressed) { await MachineOperator.StartDispenserJogging(new DispenserJoggingRequest() { Code = item.TechDispenser.Code, Direction = MotorDirection.Backward, }); } else if (action == MotorActionType.BackwardReleased) { await MachineOperator.StopDispenserJogging(new DispenserAbortJoggingRequest() { Code = item.TechDispenser.Code, }); } else if (action == MotorActionType.HomingStarted) { item.HomingProgress = 0; item.IsHoming = true; item.IsHomingCompleted = false; MachineOperator.StartDispenserHoming(new DispenserHomingRequest() { Code = item.TechDispenser.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.StopDispenserHoming(new DispenserAbortHomingRequest() { Code = item.TechDispenser.Code, }); item.IsHoming = false; } }; } private void InitDigitalOutItem(DigitalOutItem item) { item.ValueChanged += async (x, value) => { try { await MachineOperator.SetDigitalOut(new SetDigitalOutRequest() { Port = item.TechIo.Port, Value = value }); } catch (Exception ex) { //TODO: Show Exception. } }; } private void InitSingleGraphitem(SingleGraphItem item, SingleGraphElementEditor editor) { editor.InnerGraph.InnerGraph.MaxPoints = Common.Helpers.GraphsHelper.GetMaxPoints(item.TechMonitor.PointsPerFrame); item.Editor = editor; GraphController controller = new GraphController(); editor.InnerGraph.Controller = controller; _singleControllers.Add(item, controller); } private void InitMultiGraphItem(MultiGraphItem item, MultiGraphElementEditor editor) { editor.InnerGraph.InnerGraph.MaxPoints = Common.Helpers.GraphsHelper.GetMaxPoints(item.TechMonitor.PointsPerFrame); item.Editor = editor; GraphMultiController controller = new GraphMultiController(); for (int i = 0; i < item.TechMonitor.ChannelCount; i++) { controller.AddSeries(new RealTimeGraphEx.DataSeries.DataYSeries() { UseFillAndStroke = true, Name = item.TechMonitor.Name.First() + (i + 1).ToString(), Stroke = new SolidColorBrush(ColorHelper.GetRandomColor()), }); } editor.InnerGraph.Controller = controller; _multiControllers.Add(item, controller); } private void InitThreadMotionItem(ThreadMotionItem item) { item.ActionExecuted += async (x, action) => { if (action == MotorActionType.ForwardPressed) { await MachineOperator.StartThreadJogging(new ThreadJoggingRequest() { Direction = MotorDirection.Forward, }); } else if (action == MotorActionType.BackwardPressed) { await MachineOperator.StartThreadJogging(new ThreadJoggingRequest() { Direction = MotorDirection.Backward, }); } else if (action == MotorActionType.ForwardReleased || action == MotorActionType.BackwardReleased) { await MachineOperator.StopThreadJogging(new ThreadAbortJoggingRequest()); } }; } private void InitMotorGroupItem(MotorGroupItem item) { item.ActionExecuted += async (x, action) => { if (action == MotorActionType.ForwardPressed) { await Task.WhenAll(item.TechMotors.Select(motor => MachineOperator.StartMotorJogging(new MotorJoggingRequest() { Code = motor.Code, Direction = MotorDirection.Forward, }))); } else if (action == MotorActionType.ForwardReleased) { await Task.WhenAll(item.TechMotors.Select(motor => MachineOperator.StopMotorJogging(new MotorAbortJoggingRequest() { Code = motor.Code, }))); } else if (action == MotorActionType.BackwardPressed) { await Task.WhenAll(item.TechMotors.Select(motor => MachineOperator.StartMotorJogging(new MotorJoggingRequest() { Code = motor.Code, Direction = MotorDirection.Backward, }))); } else if (action == MotorActionType.BackwardReleased) { await Task.WhenAll(item.TechMotors.Select(motor => MachineOperator.StopMotorJogging(new MotorAbortJoggingRequest() { Code = 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; //} }; } #endregion #region Public Methods 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); } } public void OpenProjectFile(String fileName) { LoadProject(MachineTechViewProject.Load(fileName)); _lastTechProjectFile = fileName; } public void LoadProject(MachineTechViewProject project) { using (_notification.PushTaskItem("Loading technician project file...")) { Elements.Clear(); _singleControllers.Clear(); _multiControllers.Clear(); foreach (var item in project.Items) { if (item is MotorGroupItem) { (item as MotorGroupItem).TechMotors = ObservablesEntitiesAdapter.Instance.TechMotors.Where(x => (item as MotorGroupItem).ItemsGuids.Contains(x.Guid)).ToObservableCollection(); } AddTechItem(item); } } } 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); } } private void SaveProjectFile(String fileName) { using (_notification.PushTaskItem("Saving technician project file...")) { MachineTechViewProject project = CreateProjectFile(); project.Save(fileName); _lastTechProjectFile = fileName; } } private void SaveProject() { if (File.Exists(_lastTechProjectFile)) { SaveProjectFile(_lastTechProjectFile); } else { SaveAsProject(); } } private MachineTechViewProject CreateProjectFile() { MachineTechViewProject project = new MachineTechViewProject(); foreach (var element in 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()); project.Items.Add(element.HostedElement as TechItem); } return project; } #endregion #region IShutdownListener public void OnShuttingDown() { InvokeUINow(() => { SettingsManager.Default.MachineStudio.TechnicianModule.LasTechProjectFile = _lastTechProjectFile; }); } #endregion } }