using MainShell.Common; using MainShell.Hardware; using MainShell.Log; using MainShell.Manual.Model; using MainShell.Models; using MainShell.Motion; using MainShell.Process; using MainShell.ProcessResult; using MainShell.Recipe.Models; using MainShell.Resources.CustomControl; using MwFramework.ManagerService; using Stylet; using StyletIoC; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using System.Windows; using MW.WorkFlow; namespace MainShell.Manual.ViewModel { public class DieRecheckDisplayItem : PropertyChangedBase { private int _stepIndex; public int StepIndex { get { return _stepIndex; } set { SetAndNotify(ref _stepIndex, value); } } private int _padRow; public int PadRow { get { return _padRow; } set { SetAndNotify(ref _padRow, value); } } private int _padColumn; public int PadColumn { get { return _padColumn; } set { SetAndNotify(ref _padColumn, value); } } private int _dieRow; public int DieRow { get { return _dieRow; } set { SetAndNotify(ref _dieRow, value); } } private int _dieColumn; public int DieColumn { get { return _dieColumn; } set { SetAndNotify(ref _dieColumn, value); } } private double _padX; public double PadX { get { return _padX; } set { SetAndNotify(ref _padX, value); } } private double _padY; public double PadY { get { return _padY; } set { SetAndNotify(ref _padY, value); } } private double _dieX; public double DieX { get { return _dieX; } set { SetAndNotify(ref _dieX, value); } } private double _dieY; public double DieY { get { return _dieY; } set { SetAndNotify(ref _dieY, value); } } private string _transPathType; public string TransPathType { get { return _transPathType; } set { SetAndNotify(ref _transPathType, value); } } private bool _isMissingBond; public bool IsMissingBond { get { return _isMissingBond; } set { SetAndNotify(ref _isMissingBond, value); } } private double _xError; public double XError { get { return _xError; } set { SetAndNotify(ref _xError, value); } } private double _yError; public double YError { get { return _yError; } set { SetAndNotify(ref _yError, value); } } private bool _isXThresholdExceeded; public bool IsXThresholdExceeded { get { return _isXThresholdExceeded; } set { SetAndNotify(ref _isXThresholdExceeded, value); } } private bool _isYThresholdExceeded; public bool IsYThresholdExceeded { get { return _isYThresholdExceeded; } set { SetAndNotify(ref _isYThresholdExceeded, value); } } private string _statusSummary; public string StatusSummary { get { return _statusSummary; } set { SetAndNotify(ref _statusSummary, value); } } } public class DieRecheckFilterState : PropertyChangedBase { private enum DieRecheckFilterRule { None = 0, MissingBondOnly, XThresholdExceededOnly, YThresholdExceededOnly, XYThresholdExceededOnly } private DieRecheckFilterRule _selectedRule; private bool _isUpdatingSelection; public bool IsMissingBondRuleSelected { get { return _selectedRule == DieRecheckFilterRule.MissingBondOnly; } set { SetSelectedRule(value, DieRecheckFilterRule.MissingBondOnly, nameof(IsMissingBondRuleSelected)); } } public bool IsXThresholdRuleSelected { get { return _selectedRule == DieRecheckFilterRule.XThresholdExceededOnly; } set { SetSelectedRule(value, DieRecheckFilterRule.XThresholdExceededOnly, nameof(IsXThresholdRuleSelected)); } } public bool IsYThresholdRuleSelected { get { return _selectedRule == DieRecheckFilterRule.YThresholdExceededOnly; } set { SetSelectedRule(value, DieRecheckFilterRule.YThresholdExceededOnly, nameof(IsYThresholdRuleSelected)); } } public bool IsXYThresholdRuleSelected { get { return _selectedRule == DieRecheckFilterRule.XYThresholdExceededOnly; } set { SetSelectedRule(value, DieRecheckFilterRule.XYThresholdExceededOnly, nameof(IsXYThresholdRuleSelected)); } } private double _xThreshold = 0.01d; public double XThreshold { get { return _xThreshold; } set { SetAndNotify(ref _xThreshold, value); } } private double _yThreshold = 0.01d; public double YThreshold { get { return _yThreshold; } set { SetAndNotify(ref _yThreshold, value); } } public void ClearSelectedRule() { UpdateSelectedRule(DieRecheckFilterRule.None, null); } private void SetSelectedRule(bool isSelected, DieRecheckFilterRule targetRule, string propertyName) { if (_isUpdatingSelection) { return; } DieRecheckFilterRule newRule = isSelected ? targetRule : DieRecheckFilterRule.None; UpdateSelectedRule(newRule, propertyName); } private void UpdateSelectedRule(DieRecheckFilterRule newRule, string propertyName) { if (_selectedRule == newRule) { if (!string.IsNullOrWhiteSpace(propertyName)) { OnPropertyChanged(propertyName); } return; } _selectedRule = newRule; _isUpdatingSelection = true; try { OnPropertyChanged(nameof(IsMissingBondRuleSelected)); OnPropertyChanged(nameof(IsXThresholdRuleSelected)); OnPropertyChanged(nameof(IsYThresholdRuleSelected)); OnPropertyChanged(nameof(IsXYThresholdRuleSelected)); } finally { _isUpdatingSelection = false; } } } public class DieRecheckViewModel : OperateViewModelBase { private readonly IEventAggregator _eventAggregator; private readonly RecipeManager _recipeManager; private readonly ProcessResultManager _processResultManager; private readonly DieRecheckService _dieRecheckService; private readonly SafeAxisMotion _safeAxisMotion; private ObservableCollection _allPointResults = new ObservableCollection(); private List _filteredPointResults = new List(); private ObservableCollection _pointResults = new ObservableCollection(); public ObservableCollection PointResults { get { return _pointResults; } set { SetAndNotify(ref _pointResults, value); } } public PaginationViewModel Pagination { get; } = new PaginationViewModel(); private RegionModel _recheckRegion = new RegionModel(); public RegionModel RecheckRegion { get { return _recheckRegion; } set { SetAndNotify(ref _recheckRegion, value); } } private DieRecheckFilterState _filterState = new DieRecheckFilterState(); public DieRecheckFilterState FilterState { get { return _filterState; } set { SetAndNotify(ref _filterState, value); } } private int _pointCount; public int PointCount { get { return _pointCount; } set { SetAndNotify(ref _pointCount, value); } } private int _filteredPointCount; public int FilteredPointCount { get { return _filteredPointCount; } set { SetAndNotify(ref _filteredPointCount, value); } } private DieRecheckDisplayItem _selectedPointResult; public DieRecheckDisplayItem SelectedPointResult { get { return _selectedPointResult; } set { SetAndNotify(ref _selectedPointResult, value); } } public DieRecheckViewModel( IEventAggregator eventAggregator, RecipeManager recipeManager, ProcessResultManager processResultManager, HardwareManager hardwareManager, DieRecheckService dieRecheckService, SafeAxisMotion safeAxisMotion) { _eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator)); _recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager)); _processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager)); if (hardwareManager == null) { throw new ArgumentNullException(nameof(hardwareManager)); } _dieRecheckService = dieRecheckService ?? throw new ArgumentNullException(nameof(dieRecheckService)); _safeAxisMotion = safeAxisMotion ?? throw new ArgumentNullException(nameof(safeAxisMotion)); _cameraAxisViewModel = IoC.Get(); _cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = hardwareManager.CameraAxisManager.TopCameraAxisDevices; FilterState.PropertyChanged += FilterState_PropertyChanged; RecheckRegion.PropertyChanged += RecheckRegion_PropertyChanged; Pagination.PageChanged += Pagination_PageChanged; LoadCurrentPoints(); } protected override void OnViewLoaded() { base.OnViewLoaded(); LoadCurrentPoints(); } public async Task StartProcess() { WorkflowContext context = new WorkflowContext(); context[WorkflowContextKeys.EventAggregator] = _eventAggregator; context[WorkflowContextKeys.RecipeManager] = _recipeManager; context[WorkflowContextKeys.ProcessResultManager] = _processResultManager; context[WorkflowContextKeys.WorkflowName] = ProcessFlowName.DieRecheckFlow; WorkflowRunCompletedEventArgs result = await RunManualActivityAsync( new DieRecheckActivity(ProcessFlowName.DieRecheckFlow, _dieRecheckService), context, false); if (!IsWorkflowCompleted(result)) { return; } LoadCurrentPoints(); } public async Task MoveToSelectedPoint() { if (SelectedPointResult == null) { LocalizedMessageBox.Show(MessageKey.DieRecheckSelectedPointRequired, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning); return; } try { HardwareDevice selectedDevice = _cameraAxisViewModel.CameraAxisDevices != null ? _cameraAxisViewModel.CameraAxisDevices.SelectedHardwareDevice : null; if (selectedDevice == null || selectedDevice.AxisX == null || selectedDevice.AxisY == null) { LocalizedMessageBox.Show(MessageKey.DieRecheckMoveDeviceUnavailable, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning); return; } string moveMessage = $"DieRecheck move to point. StepIndex={SelectedPointResult.StepIndex}, PadX={SelectedPointResult.PadX:F4}, PadY={SelectedPointResult.PadY:F4}"; moveMessage.LogProcessInfo(); await Task.Run(() => _safeAxisMotion.SafeMove( MotionMoveRequest.ForAxis(selectedDevice.AxisX, SelectedPointResult.PadX, source: nameof(DieRecheckViewModel), tags: new[] { "DieRecheck", "MoveToPoint", "AxisX" }), MotionMoveRequest.ForAxis(selectedDevice.AxisY, SelectedPointResult.PadY, source: nameof(DieRecheckViewModel), tags: new[] { "DieRecheck", "MoveToPoint", "AxisY" }))); } catch (Exception exception) { LogManager.LogSysError(exception); MwMessageBox.Show(exception.Message, LanguageResourceHelper.GetString(MessageKey.TitleError), MessageBoxButton.OK, MessageBoxImage.Error); } } public void ResetFilters() { FilterState.ClearSelectedRule(); FilterState.XThreshold = 0.01d; FilterState.YThreshold = 0.01d; ApplyFilters(); } private void FilterState_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { UpdateThresholdFlags(); Pagination.CurrentPage = 1; ApplyFilters(); } private void RecheckRegion_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { Pagination.CurrentPage = 1; ApplyFilters(); } private void Pagination_PageChanged(object sender, EventArgs e) { LoadPagedData(); } private void LoadCurrentPoints() { _allPointResults.Clear(); _filteredPointResults.Clear(); PointResults.Clear(); DieRecheckProcessResult processResult = _processResultManager.DieRecheckResult; if (processResult == null || processResult.PointResults == null) { PointCount = 0; FilteredPointCount = 0; Pagination.TotalItems = 0; return; } foreach (DieRecheckPointResult pointResult in processResult.PointResults) { _allPointResults.Add(new DieRecheckDisplayItem { StepIndex = pointResult.StepIndex, PadRow = pointResult.PadRow, PadColumn = pointResult.PadColumn, DieRow = pointResult.DieRow, DieColumn = pointResult.DieColumn, PadX = pointResult.PadX, PadY = pointResult.PadY, DieX = pointResult.DieX, DieY = pointResult.DieY, TransPathType = pointResult.TransPathType, IsMissingBond = pointResult.IsMissingBond, XError = pointResult.XError, YError = pointResult.YError }); } PointCount = _allPointResults.Count; InitializeRegion(); UpdateThresholdFlags(); Pagination.CurrentPage = 1; ApplyFilters(); } private void InitializeRegion() { if (_allPointResults.Count == 0) { RecheckRegion.StartRow = 1; RecheckRegion.StartCol = 1; RecheckRegion.EndRow = 1; RecheckRegion.EndCol = 1; return; } RecheckRegion.StartRow = _allPointResults.Min(item => item.PadRow); RecheckRegion.StartCol = _allPointResults.Min(item => item.PadColumn); RecheckRegion.EndRow = Math.Max(RecheckRegion.StartRow, _allPointResults.Max(item => item.PadRow)); RecheckRegion.EndCol = Math.Max(RecheckRegion.StartCol, _allPointResults.Max(item => item.PadColumn)); } private void UpdateThresholdFlags() { foreach (DieRecheckDisplayItem item in _allPointResults) { item.IsXThresholdExceeded = Math.Abs(item.XError) >= FilterState.XThreshold; item.IsYThresholdExceeded = Math.Abs(item.YError) >= FilterState.YThreshold; item.StatusSummary = BuildStatusSummary(item); } } private string BuildStatusSummary(DieRecheckDisplayItem item) { Collection tags = new Collection(); if (item.IsMissingBond) { tags.Add(LanguageResourceHelper.GetString(MessageKey.DieRecheckStatusMissingBond)); } if (item.IsXThresholdExceeded) { tags.Add(LanguageResourceHelper.Format(MessageKey.DieRecheckStatusXExceeded, item.XError)); } if (item.IsYThresholdExceeded) { tags.Add(LanguageResourceHelper.Format(MessageKey.DieRecheckStatusYExceeded, item.YError)); } if (tags.Count == 0) { tags.Add(LanguageResourceHelper.GetString(MessageKey.DieRecheckStatusOk)); } return string.Join(" / ", tags); } private void ApplyFilters() { _filteredPointResults = _allPointResults.Where(MatchesFilters).ToList(); FilteredPointCount = _filteredPointResults.Count; Pagination.TotalItems = FilteredPointCount; if (Pagination.TotalItems == 0) { SelectedPointResult = null; PointResults = new ObservableCollection(); return; } if (Pagination.CurrentPage > Pagination.TotalPages) { Pagination.CurrentPage = Pagination.TotalPages; return; } LoadPagedData(); } private void LoadPagedData() { if (_filteredPointResults == null || _filteredPointResults.Count == 0) { PointResults = new ObservableCollection(); SelectedPointResult = null; return; } int safePage = Math.Max(1, Math.Min(Pagination.CurrentPage, Pagination.TotalPages)); int offset = (safePage - 1) * Pagination.PageSize; int limit = Pagination.PageSize; PointResults = new ObservableCollection(_filteredPointResults.Skip(offset).Take(limit)); if (SelectedPointResult != null && !PointResults.Contains(SelectedPointResult)) { SelectedPointResult = null; } } private bool MatchesFilters(DieRecheckDisplayItem item) { if (item == null) { return false; } bool isInRegion = item.PadRow >= RecheckRegion.StartRow && item.PadRow <= RecheckRegion.EndRow && item.PadColumn >= RecheckRegion.StartCol && item.PadColumn <= RecheckRegion.EndCol; if (!isInRegion) { return false; } if (FilterState.IsMissingBondRuleSelected && !item.IsMissingBond) { return false; } if (FilterState.IsXYThresholdRuleSelected && !(item.IsXThresholdExceeded && item.IsYThresholdExceeded)) { return false; } if (FilterState.IsXThresholdRuleSelected && !item.IsXThresholdExceeded) { return false; } if (FilterState.IsYThresholdRuleSelected && !item.IsYThresholdExceeded) { return false; } return true; } } }