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<ProcessInfo> ProcessInfoList { get; set; }
|
|
public Recipe()
|
{
|
Name = string.Empty;
|
ProcessInfoList = new List<ProcessInfo>();
|
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<Coord> ProcessList { get; set; }
|
|
public ProcessInfo()
|
{
|
ProcessList = new List<Coord>();
|
|
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;
|
}
|
}
|
}
|