using MainShell.Common; using MainShell.HeightMeasure.Model; using MainShell.HeightMeasure.Service; using MainShell.Log; using MainShell.Models; using MainShell.ProcessResult; using MainShell.Recipe.Models; using MainShell.Recipe.Models.SubstrateParameter; using MW.WorkFlow; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace MainShell.Process { public class SubstrateHeightMeasureService { private readonly IHeightMeasure _heightMeasure; private readonly HeightMeasureMotionService _heightMeasureMotionService; public SubstrateHeightMeasureService(IHeightMeasure heightMeasure, HeightMeasureMotionService heightMeasureMotionService) { _heightMeasure = heightMeasure ?? throw new ArgumentNullException(nameof(heightMeasure)); _heightMeasureMotionService = heightMeasureMotionService ?? throw new ArgumentNullException(nameof(heightMeasureMotionService)); } public async Task ExecuteAsync(WorkflowContext context, ActivityControl activityControl) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (activityControl == null) { throw new ArgumentNullException(nameof(activityControl)); } RecipeManager recipeManager = context.GetData(WorkflowContextKeys.RecipeManager); ProcessResultManager processResultManager = context.GetData(WorkflowContextKeys.ProcessResultManager); SubstrateRecipe substrateRecipe = GetCurrentRecipe(recipeManager); IList points = GetAvailablePoints(substrateRecipe); SubstrateHeightMeasureProcessResult processResult = InitializeProcessResult(processResultManager, substrateRecipe.RecipeName); try { LogManager.LogProcessInfo($"SubstrateHeightMeasure: start. Recipe={substrateRecipe.RecipeName}, PointCount={points.Count}"); for (int index = 0; index < points.Count; index++) { SubstrateHeightMeasurePoint point = points[index]; string pointName = GetPointName(point, index); await CheckCancellationAndPauseAsync(activityControl).ConfigureAwait(false); double positionX; double positionY; ResolveMeasurePosition(substrateRecipe, point, pointName, out positionX, out positionY); LogManager.LogProcessDebug($"SubstrateHeightMeasure: moving to point[{index}] '{pointName}', X={positionX:F4}, Y={positionY:F4}"); _heightMeasureMotionService.MoveWaferMeasurePose(positionX, positionY); double heightValue = _heightMeasure.GetDistance(); processResult.PointResults.Add(new SubstrateHeightMeasurePointResult { PointName = pointName, RowIndex = point.RowIndex, ColumnIndex = point.ColumnIndex, PositionX = positionX, PositionY = positionY, HeightValue = heightValue }); LogManager.LogProcessInfo($"SubstrateHeightMeasure: point[{index}] '{pointName}' measured height={heightValue:F4}"); } processResult.IsSuccess = true; processResult.ErrorMessage = null; processResult.ErrorMessageKey = MessageKey.None; processResult.ErrorMessageArguments = Array.Empty(); LogManager.LogProcessInfo($"SubstrateHeightMeasure: completed. Recipe={substrateRecipe.RecipeName}, PointCount={processResult.PointResults.Count}"); } catch (OperationCanceledException) { processResult.IsSuccess = false; processResult.ErrorMessage = null; processResult.ErrorMessageKey = MessageKey.None; processResult.ErrorMessageArguments = Array.Empty(); LogManager.LogProcessInfo("SubstrateHeightMeasure: operation canceled."); throw; } catch (LocalizedProcessException ex) { processResult.IsSuccess = false; processResult.ErrorMessageKey = ex.FailureMessageKey; processResult.ErrorMessageArguments = ex.FailureMessageArguments; processResult.ErrorMessage = ex.Message; LogManager.LogProcessError($"SubstrateHeightMeasure: {ex.Message}"); } catch (Exception ex) { processResult.IsSuccess = false; processResult.ErrorMessageKey = MessageKey.ProcessSubstrateHeightMeasureFailedWithReason; processResult.ErrorMessageArguments = new string[] { ex.Message ?? string.Empty }; processResult.ErrorMessage = LanguageResourceHelper.Format( processResult.ErrorMessageKey, LocalizedProcessException.ConvertArguments(processResult.ErrorMessageArguments)); LogManager.LogProcessError($"SubstrateHeightMeasure: {processResult.ErrorMessage}"); } finally { WriteResultToContext(context, processResultManager, processResult); } } private static SubstrateRecipe GetCurrentRecipe(RecipeManager recipeManager) { if (recipeManager == null) { throw new ArgumentNullException(nameof(recipeManager)); } if (recipeManager.CurrentSubstrateRecipe == null) { throw new LocalizedProcessException( MessageKey.ProcessSubstrateHeightMeasureRecipeNotLoaded, Array.Empty()); } return recipeManager.CurrentSubstrateRecipe; } private static IList GetAvailablePoints(SubstrateRecipe substrateRecipe) { if (substrateRecipe == null) { throw new ArgumentNullException(nameof(substrateRecipe)); } if (substrateRecipe.HeightMeasureSetting == null || substrateRecipe.HeightMeasureSetting.Points == null) { throw new LocalizedProcessException( MessageKey.ProcessSubstrateHeightMeasureSettingMissing, Array.Empty()); } List points = new List(); foreach (SubstrateHeightMeasurePoint point in substrateRecipe.HeightMeasureSetting.Points) { if (point != null) { points.Add(point); } } if (points.Count == 0) { throw new LocalizedProcessException( MessageKey.ProcessSubstrateHeightMeasureNoPoints, Array.Empty()); } return points; } private static SubstrateHeightMeasureProcessResult InitializeProcessResult(ProcessResultManager processResultManager, string recipeName) { if (processResultManager == null) { throw new ArgumentNullException(nameof(processResultManager)); } SubstrateHeightMeasureProcessResult processResult = processResultManager.SubstrateHeightMeasureResult; processResult.IsSuccess = false; processResult.RecipeName = recipeName; processResult.ErrorMessage = null; processResult.ErrorMessageKey = MessageKey.None; processResult.ErrorMessageArguments = Array.Empty(); processResult.PointResults.Clear(); return processResult; } private static async Task CheckCancellationAndPauseAsync(ActivityControl activityControl) { activityControl.ThrowIfCancellationRequested(); await activityControl.CheckPauseAsync().ConfigureAwait(false); } private static void ResolveMeasurePosition(SubstrateRecipe substrateRecipe, SubstrateHeightMeasurePoint point, string pointName, out double positionX, out double positionY) { if (substrateRecipe == null) { throw new ArgumentNullException(nameof(substrateRecipe)); } if (point == null) { throw new ArgumentNullException(nameof(point)); } if (substrateRecipe.HeightMeasureSetting != null && substrateRecipe.HeightMeasureSetting.Mode == SubstrateHeightMeasureMode.RowColumnOffset) { if (substrateRecipe.SubstrateInfo == null || substrateRecipe.SubstrateInfo.PitchX <= 0 || substrateRecipe.SubstrateInfo.PitchY <= 0) { throw new LocalizedProcessException( MessageKey.ProcessSubstrateHeightMeasurePointPositionInvalid, pointName); } if (point.RowIndex <= 0 || point.ColumnIndex <= 0) { throw new LocalizedProcessException( MessageKey.ProcessSubstrateHeightMeasurePointPositionInvalid, pointName); } MPoint commonOffset = substrateRecipe.HeightMeasureSetting.CommonOffsetCompensation ?? new MPoint(); MPoint pointOffset = point.OffsetCompensation ?? new MPoint(); positionX = (point.ColumnIndex - 1) * substrateRecipe.SubstrateInfo.PitchX + commonOffset.X + pointOffset.X; positionY = (point.RowIndex - 1) * substrateRecipe.SubstrateInfo.PitchY + commonOffset.Y + pointOffset.Y; return; } if (point.TeachPosition == null) { throw new LocalizedProcessException( MessageKey.ProcessSubstrateHeightMeasurePointPositionInvalid, pointName); } positionX = point.TeachPosition.X; positionY = point.TeachPosition.Y; } private static string GetPointName(SubstrateHeightMeasurePoint point, int index) { if (point == null || string.IsNullOrWhiteSpace(point.PointName)) { return $"Point{index + 1}"; } return point.PointName; } private static void WriteResultToContext( WorkflowContext context, ProcessResultManager processResultManager, SubstrateHeightMeasureProcessResult processResult) { context.SetData(WorkflowContextKeys.SubstrateHeightMeasureResult, processResult); processResultManager.SaveSubstrateHeightMeasureResult(); } } }