private void bn_Test2_Click(object sender, RoutedEventArgs e)
        {
            g_VisionManager._CamManager.fn_SetSimulFrame(ac_Align.fn_GetImageStream());
            ST_VISION_RESULT vResult = new ST_VISION_RESULT();

            g_VisionManager.fn_PreAlign(ref vResult, true);
            fn_WriteResult($"X : {vResult.pntModel.X:F3} mm, Y : {vResult.pntModel.Y:F3} mm, Score : {vResult.dScore:F3}, Angle : {vResult.dTheta:F2}");
        }
        //---------------------------------------------------------------------------

        void fn_ManualAlign(Rect rect)
        {
            // Manual align이라고 Log 표시 할 것. (Lot Log)
            // - Map 바꿔줘야함 => Align을 Polishing으로.
            // Inposition Check해야함.
            int step_result = 0;
            //bool bErr = false;
            double dTiltInterpolation = 0.0;
            Point  pntStart           = new Point();
            Point  pntEnd             = new Point();

            Point pntMillingStart = new Point();
            Point pntMillingEnd   = new Point();

            Point pntModelSpan = new Point();

            //ST_RESULT Result = new ST_RESULT();
            ST_VISION_RESULT stvisionResult = new ST_VISION_RESULT(0);

            double dPlateCenterX = MOTR[(int)EN_MOTR_ID.miSPD_X].GetPosToCmdId(EN_COMD_ID.User1);
            double dPlateCenterY = MOTR[(int)EN_MOTR_ID.miPOL_Y].GetPosToCmdId(EN_COMD_ID.User1);

            double dPlateInterpolationPosX = dPlateCenterX;
            double dPlateInterpolationPosY = 0.0;

            double dXOffset = 7;
            double dYOffset = 7;

            for (int step = 0; step < g_VisionManager.CurrentRecipe.Model[0].MillingCount; step++)
            {
                dTiltInterpolation = g_VisionManager.fn_GetTiltInterpolation(g_VisionManager.CurrentRecipe.Model[0].Milling[step].Tilt);
                fn_WriteLog(this.Title + $"Tilt Interpolation : {dTiltInterpolation.ToString("0.000")}", UserEnum.EN_LOG_TYPE.ltVision);

                pntStart.X = 0;
                pntStart.Y = 0;
                pntEnd.X   = 0;
                pntEnd.Y   = 0;

                SEQ_SPIND.vresult.stRecipeList[step_result].nUseMilling   = g_VisionManager.CurrentRecipe.Model[0].Milling[step].Enable;
                SEQ_SPIND.vresult.stRecipeList[step_result].dTilt         = g_VisionManager.CurrentRecipe.Model[0].Milling[step].Tilt;
                SEQ_SPIND.vresult.stRecipeList[step_result].dRPM          = g_VisionManager.CurrentRecipe.Model[0].Milling[step].RPM;
                SEQ_SPIND.vresult.stRecipeList[step_result].dForce        = g_VisionManager.CurrentRecipe.Model[0].Milling[step].Force;
                SEQ_SPIND.vresult.stRecipeList[step_result].dSpeed        = g_VisionManager.CurrentRecipe.Model[0].Milling[step].Speed;
                SEQ_SPIND.vresult.stRecipeList[step_result].dPitch        = g_VisionManager.CurrentRecipe.Model[0].Milling[step].Pitch;
                SEQ_SPIND.vresult.stRecipeList[step_result].nPathCnt      = g_VisionManager.CurrentRecipe.Model[0].Milling[step].PathCount;
                SEQ_SPIND.vresult.stRecipeList[step_result].nCycle        = g_VisionManager.CurrentRecipe.Model[0].Milling[step].Cycle;
                SEQ_SPIND.vresult.stRecipeList[step_result].nUtilType     = g_VisionManager.CurrentRecipe.Model[0].Milling[step].UtilType;
                SEQ_SPIND.vresult.stRecipeList[step_result].nUseUtilFill  = g_VisionManager.CurrentRecipe.Model[0].Milling[step].UtilFill;
                SEQ_SPIND.vresult.stRecipeList[step_result].nUseUtilDrain = g_VisionManager.CurrentRecipe.Model[0].Milling[step].UtilDrain;
                SEQ_SPIND.vresult.stRecipeList[step_result].nUseImage     = g_VisionManager.CurrentRecipe.Model[0].Milling[step].MillingImage;
                SEQ_SPIND.vresult.stRecipeList[step_result].nUseEPD       = g_VisionManager.CurrentRecipe.Model[0].Milling[step].EPD;
                SEQ_SPIND.vresult.stRecipeList[step_result].nUseToolChg   = g_VisionManager.CurrentRecipe.Model[0].Milling[step].ToolChange;
                SEQ_SPIND.vresult.stRecipeList[step_result].nToolType     = g_VisionManager.CurrentRecipe.Model[0].Milling[step].ToolType;

                SEQ_SPIND.vresult.stRecipeList[step_result].dTiltOffset = dTiltInterpolation;
                //vs.stRecipeList[step_result].nUtility = CurrentRecipe.Model[nModelNum].Milling[step].Util;
                // Milling Rect의 좌상단 좌표 1사분면으로 변환. (Start End Position 계산 때문에 Rect 중심계산 생략)
                pntMillingStart = fn_GetPositionFromImageCenter(
                    new Point(
                        g_VisionManager.CurrentRecipe.Model[0].Milling[step].MilRect.Left,
                        g_VisionManager.CurrentRecipe.Model[0].Milling[step].MilRect.Top)
                    , g_VisionManager._RecipeVision.CamWidth, g_VisionManager._RecipeVision.CamHeight);

                pntMillingEnd = fn_GetPositionFromImageCenter(
                    new Point(
                        g_VisionManager.CurrentRecipe.Model[0].Milling[step].MilRect.Right,
                        g_VisionManager.CurrentRecipe.Model[0].Milling[step].MilRect.Bottom)
                    , g_VisionManager._RecipeVision.CamWidth, g_VisionManager._RecipeVision.CamHeight);
                fn_WriteLog(this.Title + $"Manual Align Step : {step}, MilPx X: {g_VisionManager.CurrentRecipe.Model[0].Milling[step].MilRect.X.ToString("0.000")}, MilPx Y: {g_VisionManager.CurrentRecipe.Model[0].Milling[step].MilRect.Y.ToString("0.000")}, pntMilling : S({pntMillingStart.X.ToString("0.000")}, {pntMillingStart.Y.ToString("0.000")}), E({pntMillingEnd.X.ToString("0.000")}, {pntMillingEnd.Y.ToString("0.000")})", UserEnum.EN_LOG_TYPE.ltVision);
                //---------------------------------------------------------------------------
                // Tilt 보상 적용 할 것.
                //---------------------------------------------------------------------------
                // Position 계산.
                // Left
                pntStart.X  = pntMillingStart.X + pntModelSpan.X;
                pntStart.X *= (g_VisionManager._RecipeVision.ResolutionX / 1000.0); // um -> mm

                // Top
                pntStart.Y  = pntMillingStart.Y + pntModelSpan.Y;
                pntStart.Y *= (g_VisionManager._RecipeVision.ResolutionY / 1000.0); // um -> mm
                pntStart.Y += dTiltInterpolation;                                   // (mm Scale)

                // Right
                pntEnd.X  = pntMillingEnd.X + pntModelSpan.X;
                pntEnd.X *= (g_VisionManager._RecipeVision.ResolutionX / 1000.0); // um -> mm

                // Bottom
                pntEnd.Y  = pntMillingEnd.Y + pntModelSpan.Y;
                pntEnd.Y *= (g_VisionManager._RecipeVision.ResolutionY / 1000.0); // um -> mm
                pntEnd.Y += dTiltInterpolation;                                   // (mm Scale)

                fn_WriteLog(this.Title + $"pntStart : ({pntStart.X.ToString("0.000")}, {pntStart.Y.ToString("0.000")}), pntEnd : ({pntEnd.X.ToString("0.000")}, {pntEnd.Y.ToString("0.000")})", UserEnum.EN_LOG_TYPE.ltVision);

                // Start Position Direction Calculation.
                switch (g_VisionManager.CurrentRecipe.Model[0].Milling[step].StartPos)
                {
                // Left-Bottom
                case 0:
                    SEQ_SPIND.vresult.stRecipeList[step_result].pStartPos.X = pntStart.X;
                    SEQ_SPIND.vresult.stRecipeList[step_result].pStartPos.Y = pntEnd.Y;
                    SEQ_SPIND.vresult.stRecipeList[step_result].pEndPos.X   = pntEnd.X;
                    SEQ_SPIND.vresult.stRecipeList[step_result].pEndPos.Y   = pntStart.Y;
                    break;

                // Right-Bottom
                case 1:
                    SEQ_SPIND.vresult.stRecipeList[step_result].pStartPos.X = pntEnd.X;
                    SEQ_SPIND.vresult.stRecipeList[step_result].pStartPos.Y = pntEnd.Y;
                    SEQ_SPIND.vresult.stRecipeList[step_result].pEndPos.X   = pntStart.X;
                    SEQ_SPIND.vresult.stRecipeList[step_result].pEndPos.Y   = pntStart.Y;
                    break;
                }
                fn_WriteLog(this.Title + $"End Direction : ({SEQ_SPIND.vresult.stRecipeList[step_result].pStartPos.X}, {SEQ_SPIND.vresult.stRecipeList[step_result].pStartPos.Y}), pntEnd : ({SEQ_SPIND.vresult.stRecipeList[step_result].pEndPos.X}, {SEQ_SPIND.vresult.stRecipeList[step_result].pEndPos.Y})", UserEnum.EN_LOG_TYPE.ltVision);
                //---------------------------------------------------------------------------
                // Theta
                //---------------------------------------------------------------------------
                SEQ_SPIND.vresult.stRecipeList[step_result].dTheta = 0;
                //---------------------------------------------------------------------------
                step_result++;
            }
            //---------------------------------------------------------------------------
            // 좌표계 변환. => Cam 기준 상대좌표. (3사분면 좌표계)
            stvisionResult.pntModel.X *= g_VisionManager._RecipeVision.ResolutionX / 1000.0;
            stvisionResult.pntModel.Y *= g_VisionManager._RecipeVision.ResolutionY / 1000.0;
            //---------------------------------------------------------------------------
            int    nTotal = stvisionResult.nTotalStep;
            double dXpos  = MOTR.GetEncPos(EN_MOTR_ID.miSPD_X);
            double dYpos  = MOTR.GetEncPos(EN_MOTR_ID.miPOL_Y);
            double dTHpos = MOTR.GetEncPos(EN_MOTR_ID.miPOL_TH);

            //Save X,Y Value
            for (int n = 0; n < nTotal; n++)
            {
                //		+ (Spindle)
                //	□ (Cam)
                // Spindle X Pos Value > Cam X Pos Value
                // Spindle이 더가야하므로 Offset + 부호

                //	+ (Spindle)
                //		□ (Cam)
                // Spindle X Pos Value < Cam X Pos Value
                // Spindle이 덜가야하므로 Offset - 부호

                stvisionResult.stRecipeList[n].dStartX = dXpos - stvisionResult.stRecipeList[n].pStartPos.X + g_VisionManager._RecipeVision.SpindleOffsetX;
                stvisionResult.stRecipeList[n].dEndX   = dXpos - stvisionResult.stRecipeList[n].pEndPos.X + g_VisionManager._RecipeVision.SpindleOffsetX;

                stvisionResult.stRecipeList[n].dStartY = dYpos + stvisionResult.stRecipeList[n].pStartPos.Y - g_VisionManager._RecipeVision.SpindleOffsetY;
                stvisionResult.stRecipeList[n].dEndY   = dYpos + stvisionResult.stRecipeList[n].pEndPos.Y - g_VisionManager._RecipeVision.SpindleOffsetY;

                fn_WriteLog($"-----------------------------Result Process------------------------", EN_LOG_TYPE.ltVision);
                fn_WriteLog($"{n} - Start Pos : {stvisionResult.stRecipeList[n].dStartX}, {stvisionResult.stRecipeList[n].dStartY}", EN_LOG_TYPE.ltVision);
                fn_WriteLog($"{n} - End Pos   : {stvisionResult.stRecipeList[n].dEndX}, {stvisionResult.stRecipeList[n].dEndY}", EN_LOG_TYPE.ltVision);
                stvisionResult.stRecipeList[n].dPosTH = dTHpos;

                //---------------------------------------------------------------------------
                // System Offset
                //---------------------------------------------------------------------------
                stvisionResult.stRecipeList[n].dStartX += FM.m_stProjectBase.dPolishOffset_X;
                stvisionResult.stRecipeList[n].dEndX   += FM.m_stProjectBase.dPolishOffset_X;
                stvisionResult.stRecipeList[n].dStartY += FM.m_stProjectBase.dPolishOffset_Y;
                stvisionResult.stRecipeList[n].dEndX   += FM.m_stProjectBase.dPolishOffset_Y;
                stvisionResult.stRecipeList[n].dTilt   += FM.m_stProjectBase.dPolishOffset_TI;

                //---------------------------------------------------------------------------
                // Theta Offset 은 추가 스탭 작업 요함.
                // => Theta 가공은 Align 이후 특정 각도를 돌려서 가공을 하기위함 이므로.
                //stvisionResult.stRecipeList[n].dPosTH += FM.m_stProjectBase.dPolishOffset_TH;
                //---------------------------------------------------------------------------

                //---------------------------------------------------------------------------
                //	Vision Align InPosition Error
                //---------------------------------------------------------------------------
                // Interpolation Tilt.
                dPlateInterpolationPosY = dPlateCenterY + g_VisionManager.fn_GetTiltInterpolation(stvisionResult.stRecipeList[n].dTilt);

                //vresult.stRecipeList[n].dTiltOffset = g_VisionManager.fn_GetTiltInterpolation(stvisionResult.stRecipeList[n].dTilt);
                //Console.WriteLine($"TiltOffset- Offset - {n}:{stvisionResult.stRecipeList[n].dTiltOffset}, Tilt : {stvisionResult.stRecipeList[n].dTilt}");
                //fn_WriteLog($"TiltOffset- Offset - {n}:{stvisionResult.stRecipeList[n].dTiltOffset}, Tilt : {stvisionResult.stRecipeList[n].dTilt}");

                if (stvisionResult.stRecipeList[n].dStartX <dPlateInterpolationPosX - dXOffset || stvisionResult.stRecipeList[n].dStartX> dPlateInterpolationPosX + dXOffset)
                {
                    fn_WriteLog($"Start X InPosition Error.{n}", EN_LOG_TYPE.ltVision);
                    //bErr = true;
                }
                if (stvisionResult.stRecipeList[n].dEndX <dPlateInterpolationPosX - dXOffset || stvisionResult.stRecipeList[n].dEndX> dPlateInterpolationPosX + dXOffset)
                {
                    //bErr = true;
                }
                if (stvisionResult.stRecipeList[n].dStartY <dPlateInterpolationPosY - dYOffset || stvisionResult.stRecipeList[n].dStartY> dPlateInterpolationPosY + dYOffset)
                {
                    //bErr = true;
                }
                if (stvisionResult.stRecipeList[n].dEndY <dPlateInterpolationPosY - dYOffset || stvisionResult.stRecipeList[n].dEndY> dPlateInterpolationPosY + dYOffset)
                {
                    //bErr = true;
                }
            }


            //File Save
            SEQ_SPIND.fn_LoadVisnResult(false);
            DM.MAGA[(int)EN_MAGA_ID.POLISH].SetTo((int)EN_PLATE_STAT.ptsPolish);
        }