using SA_LTT.Module; using System; using System.Collections.Generic; namespace SA_LTT.Info.RecipeInfo { public class Recipe { public string Name { get; set; } public float Radius { get; set; } public float DistanceFromCenterToPrimaryFlat { get; set; } public float EdgeRound { get; set; } public float BeamWidth { get; set; } public float BeamHeight { get; set; } public List ProcessInfoList { get; set; } public Recipe() { Name = string.Empty; ProcessInfoList = new List(); ProcessInfoList.Clear(); Radius = 75; DistanceFromCenterToPrimaryFlat = 30; EdgeRound = 0; BeamWidth = 0.1f; BeamHeight = 0.1f; } public void AddProcessInfo(ProcessInfo processInfo) { processInfo.Radius = Radius; processInfo.DistanceFromCenterToPrimaryFlat = DistanceFromCenterToPrimaryFlat; processInfo.EdgeRound = EdgeRound; processInfo.BeamWidth = BeamWidth; ProcessInfoList.Add(processInfo.Clone()); } public Recipe Clone() { Recipe clone = new Recipe(); clone.Name = this.Name; clone.Radius = this.Radius; clone.DistanceFromCenterToPrimaryFlat = this.DistanceFromCenterToPrimaryFlat; clone.EdgeRound = this.EdgeRound; clone.BeamWidth = this.BeamWidth; clone.BeamHeight = this.BeamHeight; clone.ProcessInfoList.AddRange(this.ProcessInfoList); return clone; } public static bool IsEquals(Recipe recipe1, Recipe recipe2) { bool check = true; if (recipe1 == null && recipe2 == null) { check = true; } else if ((recipe1 == null && recipe2 != null) || (recipe1 != null && recipe2 == null)) { check = false; } else { check &= recipe1.Name == recipe2.Name; check &= recipe1.Radius == recipe2.Radius; check &= recipe1.DistanceFromCenterToPrimaryFlat == recipe2.DistanceFromCenterToPrimaryFlat; check &= recipe1.EdgeRound == recipe2.EdgeRound; check &= recipe1.BeamWidth == recipe2.BeamWidth; check &= recipe1.BeamHeight == recipe2.BeamHeight; if (recipe1.ProcessInfoList.Count == recipe2.ProcessInfoList.Count) { for (int i = 0; i < recipe1.ProcessInfoList.Count; i++) { check &= ProcessInfo.IsEquals(recipe1.ProcessInfoList[i], recipe2.ProcessInfoList[i]); if (check == false) break; } } else { check = false; } } return check; } } public struct Coord { public ScanMode ScanMode { get; set; } public double X { get; set; } public double Y { get; set; } public Coord(ScanMode scanMode, double x, double y) { this.ScanMode = scanMode; this.X = x; this.Y = y; } } public class ProcessInfo { public double Radius { get; set; } public double DistanceFromCenterToPrimaryFlat { get; set; } public double EdgeRound { get; set; } public float StartX { get; set; } public float StartY { get; set; } public float Width { get; set; } public float Height { get; set; } public float ProcessStartX { get; set; } public float ProcessStartY { get; set; } public float ProcessEndX { get; set; } public float ProcessEndY { get; set; } public float BeamWidth { get; set; } public float BeamHeight { get; set; } public float BeamHeightOverlap { get; set; } public float BeamWidthOverlap { get; set; } public double ScannerProcessSpeed { get { return (BeamHeight - BeamHeightOverlap) * 1000; } } public double Energy { get; set; } public bool IsProcessEnable { get; set; } public double ProcessLength { get; set; } public double AccTime { get; set; } public List ProcessList { get; set; } public ProcessInfo() { ProcessList = new List(); Radius = 75; DistanceFromCenterToPrimaryFlat = 70; EdgeRound = 0; StartX = 0; StartY = 0; Height = 0; Width = 0; BeamWidth = 0.1f; BeamHeight = 0.1f; AccTime = 100; SetProcessData(); } public void SetRecipeData(Recipe recipe) { Radius = recipe.Radius; DistanceFromCenterToPrimaryFlat = recipe.DistanceFromCenterToPrimaryFlat; EdgeRound = recipe.EdgeRound; BeamWidth = recipe.BeamWidth; BeamHeight = recipe.BeamHeight; } //가공 생성, 언제 할 지 애매 ... 데이타 변경 때 마다 해야되려나 ... public void SetProcessData() { float beamHeight = BeamHeight - BeamHeightOverlap; beamHeight = float.Parse($"{beamHeight:F4}"); if (Height % beamHeight != 0) //가공 높이가 beamHeight과 맞게 안떨어지면 딱 맞게 떨어지도록 변경. { int beamCount = (int)(Height / beamHeight); if (Height % beamHeight > beamHeight / 2) { Height = (beamCount + 1) * beamHeight; } else { Height = beamCount * beamHeight; } } if(IsProcessingAreaContainWafer()) { if(GetProcessStartEndCoords()) { CreateProcessLines(); } } else { ProcessStartX = 0; ProcessStartY = 0; ProcessEndX = 0; ProcessEndY = 0; ProcessList.Clear(); } } //가공 범위가 웨이퍼 안에 있는지 확인. private bool IsProcessingAreaContainWafer() { bool isContainsEllipse = true; //웨이퍼 중심과 사각형 중심 사이의 거리. double left = StartX; double top = StartY; double right = left + Width; double bottom = top - Height; // 0, 0 <- 중심. if ((left <= 0 && 0 <= right) || (bottom <= 0 && 0 <= top)) { double processsRadius = Radius - EdgeRound; //사각형 반지름 만큼 확장 double extendRectLeft = left - processsRadius; double extendRectTop = top + processsRadius; double extendRectRight = right + processsRadius; double extendRectBottom = bottom - processsRadius; //확장된 사각형 안에 중심이 오면 충돌 if ((extendRectLeft < 0 && 0 < extendRectRight) && (extendRectBottom < 0 && 0 < extendRectTop)) { if (top < -DistanceFromCenterToPrimaryFlat) { isContainsEllipse = false; } else { isContainsEllipse = true; } } else { isContainsEllipse = false; } } else { //사각형의 꼭지점이 원 안에 오면 충돌 if (IsCoordInWafer(left, top) || IsCoordInWafer(right, top) || IsCoordInWafer(left, bottom) || IsCoordInWafer(right, bottom)) { if (top < -(DistanceFromCenterToPrimaryFlat - EdgeRound)) { isContainsEllipse = false; } else { isContainsEllipse = true; } } else { isContainsEllipse = false; } } IsProcessEnable = isContainsEllipse; return isContainsEllipse; } //가공 범위가 웨이퍼 안에 있으면 가공 시작 종료 위치 얻음. private bool GetProcessStartEndCoords() { if (IsProcessEnable == false) //가공 범위가 웨이퍼 안에 없으면 false 반환. return false; double processsRadius = Radius - EdgeRound; double startX, startY; double endX, endY; double left = StartX; double top = StartY; double right = left + Width; double bottom = top - Height; startX = 0; startY = 0; endX = 0; endY = 0; //=================== Start X, Y 구하기 ========================= if (top >= 0) { if (top - Height > 0) { startX = -Math.Sqrt(Math.Pow(processsRadius, 2) - Math.Pow(top - Height, 2)); } else { startX = -Math.Sqrt(Math.Pow(processsRadius, 2)); } } else { startX = -Math.Sqrt(Math.Pow(processsRadius, 2) - Math.Pow(top, 2)); } //Start X if (left < 0 && startX < left) { startX = left; } else if (left >= 0 && startX < left) { startX = left; } //Start Y if (top > processsRadius) { startY = processsRadius; } else { startY = top; } //================================================== //=================== End X, Y 구하기 ========================= if (top >= 0) { if (top - Height > 0) { //startY = top - Height; endX = Math.Sqrt(Math.Pow(processsRadius, 2) - Math.Pow(top - Height, 2)); } else { endX = Math.Sqrt(Math.Pow(processsRadius, 2)); } //startY = top; } else { endX = Math.Sqrt(Math.Pow(processsRadius, 2) - Math.Pow(top, 2)); } //End X if (right <= processsRadius && right < endX) { endX = right; } //End Y if ( (top - Height) >= processsRadius) { endY = processsRadius; } else { endY = top - Height; } if (endY < -(DistanceFromCenterToPrimaryFlat - EdgeRound)) { double beamHeightSize = BeamHeight - BeamHeightOverlap; double endHeight = top + (DistanceFromCenterToPrimaryFlat - EdgeRound); int beamCount = (int)(endHeight / beamHeightSize); if (endHeight % beamHeightSize > beamHeightSize / 2) { beamCount++; } endY = top - (beamCount * beamHeightSize); } ProcessStartX = float.Parse($"{startX:F4}"); ProcessStartY = float.Parse($"{startY:F4}"); ProcessEndX = float.Parse($"{endX:F4}"); ProcessEndY = float.Parse($"{endY:F4}"); return true; } //좌표가 웨이퍼 안에 있는지 확인 private bool IsCoordInWafer(double x, double y) { return Math.Pow(x, 2) + Math.Pow(y, 2) <= Math.Pow(Radius - EdgeRound, 2); } //X축을 기점으로 웨이퍼 접점 Y위치 반환. private double GetPointYOfContactOfEllips(double x, double y) { double position = 0; x = double.Parse($"{x:F4}"); y = double.Parse($"{y:F4}"); position = Math.Sqrt(Math.Pow(Radius - EdgeRound, 2) - Math.Pow(x, 2)); position = y < 0 ? -position : position; position = double.Parse($"{position:F4}"); return position; } //가공 좌표 List 생성 private bool CreateProcessLines() { if (IsProcessEnable == false) //가공 범위가 웨이퍼 안에 없으면 false 반환. return false; int oddCheck = 0; ProcessLength = 0; double jumpY = 0; double markY = 0; float beamHeightSize = BeamHeight - BeamHeightOverlap; beamHeightSize = float.Parse($"{beamHeightSize:F4}"); ProcessList.Clear(); // 기존 데이터 초기화 bool _accLengthCheck = true; for (double i = ProcessStartX; i <= ProcessEndX; i += (BeamWidth - BeamWidthOverlap)) // X축 방향 탐색 { //가공 시작 기준 if (ProcessEndY >= 0) // 가공 End 가 0보다 위일 때, End위치 기준으로. { if (oddCheck % 2 == 0) // 짝수 (left top, start 위치에서 시작) { //Jump if (IsCoordInWafer(i, ProcessEndY + Height)) { jumpY = ProcessEndY + Height; } else { jumpY = GetPointYOfContactOfEllips(i, ProcessEndY + Height); if((jumpY % beamHeightSize) >= (beamHeightSize / 2)) { jumpY = ProcessEndY + (beamHeightSize * Math.Floor((jumpY - ProcessEndY) / beamHeightSize)); } else { jumpY = ProcessEndY + (beamHeightSize * Math.Ceiling((jumpY - ProcessEndY) / beamHeightSize)); } } //Mark if (IsCoordInWafer(i, ProcessEndY)) { markY = ProcessEndY; } else { markY = GetPointYOfContactOfEllips(i, ProcessEndY); if ((markY % beamHeightSize) >= (beamHeightSize / 2)) { markY = ProcessEndY + (beamHeightSize * Math.Floor((markY - ProcessEndY) / beamHeightSize)); } else { markY = ProcessEndY + (beamHeightSize * Math.Ceiling((markY - ProcessEndY) / beamHeightSize)); } } } else // 홀수 (right bottom, 위치에서 시작) { //Jump if (IsCoordInWafer(i, ProcessEndY)) { jumpY = ProcessEndY; } else { jumpY = GetPointYOfContactOfEllips(i, ProcessEndY); if ((jumpY % beamHeightSize) >= (beamHeightSize / 2)) { jumpY = ProcessEndY + (beamHeightSize * Math.Floor((jumpY - ProcessEndY) / beamHeightSize)); } else { jumpY = ProcessEndY + (beamHeightSize * Math.Ceiling((jumpY - ProcessEndY) / beamHeightSize)); } } //Mark if (IsCoordInWafer(i, ProcessEndY + Height)) { markY = ProcessEndY + Height; } else { markY = GetPointYOfContactOfEllips(i, ProcessEndY + Height); if ((markY % beamHeightSize) >= (beamHeightSize / 2)) { markY = ProcessEndY + (beamHeightSize * Math.Floor((markY - ProcessEndY) / beamHeightSize)); } else { markY = ProcessEndY + (beamHeightSize * Math.Ceiling((markY - ProcessEndY) / beamHeightSize)); } } } if(_accLengthCheck && jumpY >= ProcessStartY) { _accLengthCheck = false; AccTime = ProcessLength / ScannerProcessSpeed; } } else // 가공 End가 0보다 밑일 때 { if (oddCheck % 2 == 0) // 짝수 (left top, start 위치에서 시작) { //Jump if (IsCoordInWafer(i, ProcessStartY)) { jumpY = ProcessStartY; } else { jumpY = GetPointYOfContactOfEllips(i, ProcessStartY); if ((jumpY % beamHeightSize) >= (beamHeightSize / 2)) { jumpY = ProcessStartY - (beamHeightSize * Math.Floor((ProcessStartY - jumpY) / beamHeightSize)); } else { jumpY = ProcessStartY - (beamHeightSize * Math.Ceiling((ProcessStartY - jumpY) / beamHeightSize)); } } //Mark if (IsCoordInWafer(i, ProcessStartY - Height)) { markY = ProcessStartY - Height; } else { markY = GetPointYOfContactOfEllips(i, ProcessStartY - Height); if ((jumpY % beamHeightSize) >= (beamHeightSize / 2)) { markY = ProcessStartY - (beamHeightSize * Math.Floor((ProcessStartY - markY) / beamHeightSize)); } else { markY = ProcessStartY - (beamHeightSize * Math.Ceiling((ProcessStartY - markY) / beamHeightSize)); } } } else // 홀수 (right bottom, 위치에서 시작) { //Jump if (IsCoordInWafer(i, ProcessStartY - Height)) { jumpY = ProcessStartY - Height; } else { jumpY = GetPointYOfContactOfEllips(i, ProcessStartY - Height); if ((jumpY % beamHeightSize) >= (beamHeightSize / 2)) { jumpY = ProcessStartY - (beamHeightSize * Math.Floor((ProcessStartY - jumpY) / beamHeightSize)); } else { jumpY = ProcessStartY - (beamHeightSize * Math.Ceiling((ProcessStartY - jumpY) / beamHeightSize)); } } //Mark if (IsCoordInWafer(i, ProcessStartY)) { markY = ProcessStartY; } else { markY = GetPointYOfContactOfEllips(i, ProcessStartY); if ((markY % beamHeightSize) >= (beamHeightSize / 2)) { markY = ProcessStartY - (beamHeightSize * Math.Floor((ProcessStartY - markY) / beamHeightSize)); } else { markY = ProcessStartY - (beamHeightSize * Math.Ceiling((ProcessStartY - markY) / beamHeightSize)); } } } if (_accLengthCheck && jumpY <= ProcessEndY) { _accLengthCheck = false; AccTime = ProcessLength / ScannerProcessSpeed; } } if (jumpY < ProcessEndY) { jumpY = ProcessEndY; } if (markY < ProcessEndY) { markY = ProcessEndY; } jumpY = double.Parse($"{jumpY:F4}"); markY = double.Parse($"{markY:F4}"); i = double.Parse($"{i:F4}"); ProcessList.Add(new Coord(ScanMode.JUMP, i, jumpY)); ProcessList.Add(new Coord(ScanMode.MARK, i, markY)); ProcessLength += Math.Abs(markY - jumpY); //ProcessLength += Math.Abs(ProcessStartY - ProcessEndY); ProcessLength += 0.1 + 0.1; // JumpDelay + MarkDelay; ProcessLength += beamHeightSize; oddCheck++; } // Process y - y = 3950.64 // mark y -y = 3723.49 ProcessLength = double.Parse($"{ProcessLength:F3}"); return true; } public ProcessInfo Clone() { ProcessInfo processInfo = new ProcessInfo(); processInfo.Radius = Radius; processInfo.DistanceFromCenterToPrimaryFlat = DistanceFromCenterToPrimaryFlat; processInfo.EdgeRound = EdgeRound; processInfo.StartX = StartX; processInfo.StartY = StartY; processInfo.Height = Height; processInfo.Width = Width; processInfo.ProcessStartX = ProcessStartX; processInfo.ProcessStartY = ProcessStartY; processInfo.ProcessEndX = ProcessEndX; processInfo.ProcessEndY = ProcessEndY; processInfo.BeamWidth = BeamWidth; processInfo.BeamHeight = BeamHeight; processInfo.BeamHeightOverlap = BeamHeightOverlap; processInfo.BeamWidthOverlap = BeamWidthOverlap; processInfo.Energy = Energy; processInfo.IsProcessEnable = IsProcessEnable; foreach(Coord coord in ProcessList) { Coord copy = new Coord(coord.ScanMode, coord.X, coord.Y); processInfo.ProcessList.Add(copy); } return processInfo; } public static bool IsEquals(ProcessInfo info1, ProcessInfo info2) { bool check = true; if (info1 == null && info2 == null) { check = true; } else if ((info1 == null && info2 != null) || (info1 != null && info2 == null)) { check = false; } else { check &= info1.Radius == info2.Radius; check &= info1.DistanceFromCenterToPrimaryFlat == info2.DistanceFromCenterToPrimaryFlat; check &= info1.StartX == info2.StartX; check &= info1.StartY == info2.StartY; check &= info1.Height == info2.Height; check &= info1.Width == info2.Width; check &= info1.ProcessStartX == info2.ProcessStartX; check &= info1.ProcessStartY == info2.ProcessStartY; check &= info1.ProcessEndX == info2.ProcessEndX; check &= info1.ProcessEndY == info2.ProcessEndY; check &= info1.BeamWidth == info2.BeamWidth; check &= info1.BeamHeightOverlap == info2.BeamHeightOverlap; check &= info1.BeamWidthOverlap == info2.BeamWidthOverlap; check &= info1.IsProcessEnable == info2.IsProcessEnable; check &= info1.Energy == info2.Energy; if (info1.ProcessList.Count == info2.ProcessList.Count) { for (int i = 0; i < info1.ProcessList.Count; i++) { check &= info1.ProcessList[i].ScanMode == info2.ProcessList[i].ScanMode; check &= info1.ProcessList[i].X == info2.ProcessList[i].X; check &= info1.ProcessList[i].Y == info2.ProcessList[i].Y; if (check == false) break; } } else { check = false; } } return check; } } }