已修改13个文件
1019 ■■■■■ 文件已修改
EdgeInspector_App/Data/DefectStorage.cpp 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/Data/DefectStorage.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/Defect.h 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/Define/Global_Define.h 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/EdgeInspector_App.rc 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/Process/InspectCamera.cpp 457 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/Process/InspectCamera.h 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/Recipe/GlassRecipe.cpp 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/Recipe/GlassRecipe.h 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/View/ViewMain_ScanImage.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/ViewMain_Recipe.cpp 312 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/ViewMain_Recipe.h 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/resource.h 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/Data/DefectStorage.cpp
@@ -410,3 +410,42 @@
    MyLock.Unlock();
    return m_nDefect;
}
int CDefectControl::ExtractDefect_RipThickness(DimensionDir emDim, int iCam, int iScan, const CDefect_Info& defectInfo, double dMeasured_um, double dSpecDiff_um)
{
    if (m_pGrabber == NULL) {
        return m_nDefect;
    }
    CSingleLock MyLock(&m_csDefect);
    MyLock.Lock();
    if (m_nDefect >= m_maxDefect) {
        MyLock.Unlock();
        return m_nDefect;
    }
    CDefect* pDefect = GetDefect(m_nDefect);
    if (pDefect == NULL) {
        MyLock.Unlock();
        return m_nDefect;
    }
    pDefect->Reset();
    pDefect->m_bJudge_NG = TRUE;            // RIP 厚度异常即 NG
    pDefect->m_nJudge_X_um = dMeasured_um;  // 记录测得厚度
    pDefect->m_nJudge_Y_um = dSpecDiff_um;  // 记录与规格差值
    pDefect->m_DefectInfo = defectInfo;
    int nXStart = defectInfo.m_ptDefectPos_pxl.x - (DEFECTIMAGE_WIDTH / 2);
    int nYStart = defectInfo.m_ptDefectPos_pxl.y - (DEFECTIMAGE_HEIGHT / 2);
    if (!m_pGrabber->GetSmallImage(iScan, pDefect->m_Image, nXStart, nYStart, DEFECTIMAGE_WIDTH, DEFECTIMAGE_HEIGHT, FALSE)) {
        MyLock.Unlock();
        return m_nDefect;
    }
    m_nDefect++;
    MyLock.Unlock();
    return m_nDefect;
}
EdgeInspector_App/Data/DefectStorage.h
@@ -50,6 +50,7 @@
    int            ExtractDefect_Mark(DimensionDir emDim, int iCam, int iScan, CDefect_Info defect);
    int         ExtractDefect_CorChamfer(DimensionDir emDim, int iCam, int iScan, DefectLocation eDefect, CDefect_Info  ptDefect, int nProfileIndex); //2024.6.12
    int            ExtractDefect_RipThickness(DimensionDir emDim, int iCam, int iScan, const CDefect_Info& defectInfo, double dMeasured_um, double dSpecDiff_um);
    void        ResetDefectControl();        
    int            CheckDefectCount();    
    int            IncreaseDefectCount(){++m_nDefect; return m_nDefect;}
EdgeInspector_App/Defect.h
@@ -37,6 +37,10 @@
    DefectLoc_License,
    DefectLoc_Rip_Thickness_Glass,
    DefectLoc_Rip_Thickness_Left,
    DefectLoc_Rip_Thickness_Right,
    DefectLoc_Unknown,
    
    DefectLoc_None
@@ -74,8 +78,13 @@
    _T("WS_Fail"),
    _T("Exception"),
    _T("License"),
    _T("Rip_GlassThickness"),
    _T("Rip_LeftGap"),
    _T("Rip_RightGap"),
    _T("Unknown")
};
EdgeInspector_App/Define/Global_Define.h
@@ -325,10 +325,11 @@
    eRcp_InsType_In_Burr, 
    eRcp_InsType_In_Chamfer, 
    eRcp_InsType_TopCorner, 
    eRcp_InsType_BotCorner
    eRcp_InsType_BotCorner,
    eRcp_InsType_RipThickness
};
static CString g_strInsType[10] =
static CString g_strInsType[] =
{
    _T("Chip"),
    _T("Crack"),
@@ -340,6 +341,7 @@
    _T("In_Chamfer"),
    _T("Top_Corner"),
    _T("Bot_Corner"),
    _T("Thickness")
};
enum eSideInsType        
EdgeInspector_App/EdgeInspector_App.rc
Binary files differ
EdgeInspector_App/Process/InspectCamera.cpp
@@ -424,6 +424,458 @@
        }
    }
#endif // HALCON_VISION_KEY
    MeasureSideEdge_EI(eDim);
}
BOOL CInspectCamera::MeasureSideEdge_EI(DimensionDir eDim) {
    /* code */
    bool bExec = (DIMENSION_A_RIP == eDim) || (DIMENSION_B_RIP == eDim) || (DIMENSION_C_RIP == eDim) || (DIMENSION_D_RIP == eDim);
    if (!bExec) return FALSE;
    CSide_Data* pSideData = m_pGlassData->GetSideData(eDim);
    //查找边界
    CFrameBufferController* pFrameBufferController = m_pGrabber->GetFrameBuffer();
    int imgWidth = pFrameBufferController->GetFrameWidth();
    //获取上边界和下边
    int nTopEdge = pSideData->m_nGlassStartLine;
    int nBottomEdge = pSideData->m_nGlassEndLine;
    g_pLog->DisplayMessage(_T("[APP]Measure Side Edge Start, StartEdge = %d, EndEdge = %d, Width = %d"), nTopEdge, nBottomEdge, imgWidth);
    stFrameIndex stFrame = m_pGrabber->GetGrabFrame();
    CRIP_THICKNESS_PARM& rip = m_pRecipe->m_SideParam[eDim].m_RipThk;
    if (rip.m_bUseInspect != TRUE) {
        return TRUE;
    }
    int inspectNum = 36;
    //输入参数
    int topOffset = rip.m_nTopOffset;        //T Offset
    int botOffset = rip.m_nBottomOffset;    //B Offset
    int leftEdge = rip.m_nRectLeft;            //Rect L
    int rightEdge = rip.m_nRectRight;        //Rect R
    int leftThres = rip.m_nLeftThres;       //Left Thres
    int rightThres = rip.m_nRightThres;     //Right Thres
    int gassThres = rip.m_nGlassThres;      //Gas Thres
    double dist_array[36];                     //输出结果
    double left_dist_Gass_array[36];           //输出结果
    double right_dist_Gass_array[36];          //输出结果
    double xMinEdge_array_px[36];              //输出结果
    double yMinEdge_array_px[36];              //输出结果
    double xMaxEdge_array_px[36];              //输出结果
    double yMaxEdge_array_px[36];              //输出结果
    double xMinGas_array_px[36];               //输出结果
    double yMinGas_array_px[36];               //输出结果
    double xMaxGas_array_px[36];               //输出结果
    double yMaxGas_array_px[36];               //输出结果
    int startY = nTopEdge + topOffset;
    int endY = nBottomEdge - botOffset;
    int step = (int)((endY - startY) / inspectNum);
    g_pLog->DisplayMessage(_T("[APP]Measure Side Edge, (startY, endY, step) = (%d, %d, %d)"), startY, endY, step);
    for (int i = 0; i < inspectNum; i++) {
        int x1 = leftEdge;
        int y1 = startY + i * step;
        int x2 = rightEdge;
        int y2 = startY + (i + 1) * step;
        double szDist, leftGassDist, rightGassDist;
        MeasureSideEdge_POS(eDim,
            x1, y1, x2, y2,
            leftThres, rightThres, gassThres,
            xMinEdge_array_px[i], yMinEdge_array_px[i], xMaxEdge_array_px[i], yMaxEdge_array_px[i],
            xMinGas_array_px[i], yMinGas_array_px[i], xMaxGas_array_px[i], yMaxGas_array_px[i],
            szDist, leftGassDist, rightGassDist);
        dist_array[i] = m_dPixelSizeX * szDist;
        left_dist_Gass_array[i] = m_dPixelSizeX * leftGassDist;
        right_dist_Gass_array[i] = m_dPixelSizeX * rightGassDist;
        g_pLog->DisplayMessage(_T("[APP]Measure Result %d: LeftEdge = (%.1f, %.1f), LeftGas = (%.1f, %.1f), rightGas = (%.1f, %.1f), RightEdge = (%.1f, %.1f), (Dist, LeftGass, RightGass) = (%.1f, %.1f, %.1f)"),
            i + 1,
            xMinEdge_array_px[i], yMinEdge_array_px[i],
            xMinGas_array_px[i], yMinGas_array_px[i],
            xMaxGas_array_px[i], yMaxGas_array_px[i],
            xMaxEdge_array_px[i], yMaxEdge_array_px[i],
            dist_array[i], left_dist_Gass_array[i], right_dist_Gass_array[i]);
        // 添加缺陷
        int nStep = max(1, (endY - startY) / inspectNum); // 防 0
        auto SpecDiff = [](double v, double vMin, double vMax)->double {
            if (v <= 0) return 0.0;            // 无效值
            if (v < vMin) return v - vMin;     // 负:低于下限
            if (v > vMax) return v - vMax;     // 正:高于上限
            return 0.0;                        // 在规格内
        };
        auto MakeBaseInfo = [&](CPoint pt, CRect rt, DefectLocation loc)->CDefect_Info {
            CDefect_Info info;
            info.m_iFrameIdx = stFrame.nFrameIdx;
            info.m_nCamID = m_iCamera;
            info.m_nScanIdx = stFrame.nScanIdx;
            info.m_nGlassStartLine = pSideData->m_nGlassStartLine;
            info.m_nSideIdx = (int)eDim;
            info.m_ptDefectPos_pxl = pt;
            info.m_rtDefectPos_pxl = rt;
            info.m_DefectLoc = loc;
            return info;
        };
        bool ngGlass = (dist_array[i] > 0) && (dist_array[i] < rip.m_nGlassSizeMin_um || dist_array[i] > rip.m_nGlassSizeMax_um);
        bool ngLeft = (left_dist_Gass_array[i] > 0) && (left_dist_Gass_array[i] < rip.m_nLeftRipSizeMin_um || left_dist_Gass_array[i] > rip.m_nLeftRipSizeMax_um);
        bool ngRight = (right_dist_Gass_array[i] > 0) && (right_dist_Gass_array[i] < rip.m_nRightRipSizeMin_um || right_dist_Gass_array[i] > rip.m_nRightRipSizeMax_um);
        // Glass 厚度 NG
        if (ngGlass) {
            CRect rt(
                (int)min(min(xMinEdge_array_px[i], xMinGas_array_px[i]), min(xMaxEdge_array_px[i], xMaxGas_array_px[i])),
                (int)min(min(yMinEdge_array_px[i], yMinGas_array_px[i]), min(yMaxEdge_array_px[i], yMaxGas_array_px[i])),
                (int)max(max(xMinEdge_array_px[i], xMinGas_array_px[i]), max(xMaxEdge_array_px[i], xMaxGas_array_px[i])),
                (int)max(max(yMinEdge_array_px[i], yMinGas_array_px[i]), max(yMaxEdge_array_px[i], yMaxGas_array_px[i]))
            );
            CPoint pt((rt.left + rt.right) / 2, (rt.top + rt.bottom) / 2);
            CDefect_Info defect = MakeBaseInfo(pt, rt, DefectLoc_Rip_Thickness_Glass);
            double dDiff = SpecDiff(dist_array[i], rip.m_nGlassSizeMin_um, rip.m_nGlassSizeMax_um);
            m_pDefectControl->ExtractDefect_RipThickness(eDim, m_iCamera, stFrame.nScanIdx, defect, dist_array[i], dDiff);
        }
        // 左侧气隙 NG
        if (ngLeft) {
            CRect rt(
                (int)min(xMinEdge_array_px[i], xMinGas_array_px[i]),
                (int)min(yMinEdge_array_px[i], yMinGas_array_px[i]),
                (int)max(xMinEdge_array_px[i], xMinGas_array_px[i]),
                (int)max(yMinEdge_array_px[i], yMinGas_array_px[i])
            );
            CPoint pt((rt.left + rt.right) / 2, (rt.top + rt.bottom) / 2);
            CDefect_Info defect = MakeBaseInfo(pt, rt, DefectLoc_Rip_Thickness_Left);
            double dDiff = SpecDiff(left_dist_Gass_array[i], rip.m_nLeftRipSizeMin_um, rip.m_nLeftRipSizeMax_um);
            m_pDefectControl->ExtractDefect_RipThickness(eDim, m_iCamera, stFrame.nScanIdx, defect, left_dist_Gass_array[i], dDiff);
        }
        // 右侧气隙 NG
        if (ngRight) {
            CRect rt(
                (int)min(xMaxEdge_array_px[i], xMaxGas_array_px[i]),
                (int)min(yMaxEdge_array_px[i], yMaxGas_array_px[i]),
                (int)max(xMaxEdge_array_px[i], xMaxGas_array_px[i]),
                (int)max(yMaxEdge_array_px[i], yMaxGas_array_px[i])
            );
            CPoint pt((rt.left + rt.right) / 2, (rt.top + rt.bottom) / 2);
            CDefect_Info defect = MakeBaseInfo(pt, rt, DefectLoc_Rip_Thickness_Right);
            double dDiff = SpecDiff(right_dist_Gass_array[i], rip.m_nRightRipSizeMin_um, rip.m_nRightRipSizeMax_um);
            m_pDefectControl->ExtractDefect_RipThickness(eDim, m_iCamera, stFrame.nScanIdx, defect, right_dist_Gass_array[i], dDiff);
        }
    }
    return TRUE;
}
void CInspectCamera::MeasureSideEdge_POS(DimensionDir eDim,
    int x1, int y1, int x2, int y2,
    int leftThres, int rightThres, int gassThres,
    double& minEdgeX, double& minEdgeY, double& maxEdgeX, double& maxEdgeY,
    double& minGasX, double& minGasY, double& maxGasX, double& maxGasY,
    double& szDist, double& leftGassDist, double& rightGassDist) {
    /* code */
    szDist = 0.0f;
    leftGassDist = 0.0f;
    rightGassDist = 0.0f;
    int width = x2 - x1 + 1;
    int height = y2 - y1 + 1;
    uchar* imgData = new uchar[width * height];
    try {
        CutImageROI(imgData, eDim, x1, y1, x2, y2);
        MeasurePoint dotResult1[100];
        MeasurePoint dotResult2[100];
        MeasurePoint dotResult3[100];
        MeasurePoint dotResult4[100];
        MeasurePoint dotResult5[100];
        MeasurePoint dotResult6[100];
        MeasurePoint dotResult7[100];
        int dotNum1 = 0;
        int dotNum2 = 0;
        int dotNum3 = 0;
        int dotNum4 = 0;
        int dotNum5 = 0;
        int dotNum6 = 0;
        int dotNum7 = 0;
        CVisionMeasureClass doExec;
        double sigma = 2.5f;   //输入参数, 增加参数
        doExec.doMeasurePosA(imgData, width, height, sigma,
            dotResult1, dotNum1,
            dotResult2, dotNum2,
            dotResult3, dotNum3,
            dotResult4, dotNum4,
            dotResult5, dotNum5,
            dotResult6, dotNum6,
            dotResult7, dotNum7);
        for (int i = 0; i < dotNum1; i++) {
            dotResult1[i].x += x1;
            dotResult1[i].y += y1;
        }
        for (int i = 0; i < dotNum2; i++) {
            dotResult2[i].x += x1;
            dotResult2[i].y += y1;
        }
        for (int i = 0; i < dotNum3; i++) {
            dotResult3[i].x += x1;
            dotResult3[i].y += y1;
        }
        for (int i = 0; i < dotNum4; i++) {
            dotResult4[i].x += x1;
            dotResult4[i].y += y1;
        }
        for (int i = 0; i < dotNum5; i++) {
            dotResult5[i].x += x1;
            dotResult5[i].y += y1;
        }
        for (int i = 0; i < dotNum6; i++) {
            dotResult6[i].x += x1;
            dotResult6[i].y += y1;
        }
        for (int i = 0; i < dotNum7; i++) {
            dotResult7[i].x += x1;
            dotResult7[i].y += y1;
        }
        int fzDist = 4.5;                 //输入参数, 增加参数
        double xMinResult[7] = { -1 };
        double yMinResult[7] = { -1 };
        double xMaxResult[7] = { -1 };
        double yMaxResult[7] = { -1 };
        double xMinGassResult[7] = { -1 };
        double yMinGassResult[7] = { -1 };
        double xMaxGassResult[7] = { -1 };
        double yMaxGassResult[7] = { -1 };
        MeasureResultProc(dotResult1, dotNum1, leftThres, rightThres, gassThres, fzDist,
            xMinResult[0], yMinResult[0], xMaxResult[0], yMaxResult[0],
            xMinGassResult[0], yMinGassResult[0], xMaxGassResult[0], yMaxGassResult[0]);
        MeasureResultProc(dotResult2, dotNum2, leftThres, rightThres, gassThres, fzDist,
            xMinResult[1], yMinResult[1], xMaxResult[1], yMaxResult[1],
            xMinGassResult[1], yMinGassResult[1], xMaxGassResult[1], yMaxGassResult[1]);
        MeasureResultProc(dotResult3, dotNum3, leftThres, rightThres, gassThres, fzDist,
            xMinResult[2], yMinResult[2], xMaxResult[2], yMaxResult[2],
            xMinGassResult[2], yMinGassResult[2], xMaxGassResult[2], yMaxGassResult[2]);
        MeasureResultProc(dotResult4, dotNum4, leftThres, rightThres, gassThres, fzDist,
            xMinResult[3], yMinResult[3], xMaxResult[3], yMaxResult[3],
            xMinGassResult[3], yMinGassResult[3], xMaxGassResult[3], yMaxGassResult[3]);
        MeasureResultProc(dotResult5, dotNum5, leftThres, rightThres, gassThres, fzDist,
            xMinResult[4], yMinResult[4], xMaxResult[4], yMaxResult[4],
            xMinGassResult[4], yMinGassResult[4], xMaxGassResult[4], yMaxGassResult[4]);
        MeasureResultProc(dotResult6, dotNum6, leftThres, rightThres, gassThres, fzDist,
            xMinResult[5], yMinResult[5], xMaxResult[5], yMaxResult[5],
            xMinGassResult[5], yMinGassResult[5], xMaxGassResult[5], yMaxGassResult[5]);
        MeasureResultProc(dotResult7, dotNum7, leftThres, rightThres, gassThres, fzDist,
            xMinResult[6], yMinResult[6], xMaxResult[6], yMaxResult[6],
            xMinGassResult[6], yMinGassResult[6], xMaxGassResult[6], yMaxGassResult[6]);
        std::vector<double> vDist, vLeftGass, vRightGass;
        for (int i = 0; i < 7; i++) {
            if (xMinResult[i] > 0 && xMaxResult[i] > 0) {
                double dx = xMaxResult[i] - xMinResult[i];
                double dy = yMaxResult[i] - yMinResult[i];
                double dist = sqrt(dx * dx + dy * dy);
                vDist.push_back(dist);
            }
            if (xMinResult[i] > 0 && xMinGassResult[i] > 0) {
                double dx = xMinGassResult[i] - xMinResult[i];
                double dy = yMinGassResult[i] - yMinResult[i];
                double dist = sqrt(dx * dx + dy * dy);
                vLeftGass.push_back(dist);
            }
            if (xMaxResult[i] > 0 && xMaxGassResult[i] > 0) {
                double dx = xMaxResult[i] - xMaxGassResult[i];
                double dy = yMaxResult[i] - yMaxGassResult[i];
                double dist = sqrt(dx * dx + dy * dy);
                vRightGass.push_back(dist);
            }
        }
        double avgDist = 0.0f;
        int sz = (int)(vDist.size());
        if (sz > 3){
            for (int i = 0; i < sz - 1; i++) {
                for (int j = i + 1; j < sz; j++) {
                    if (vDist[i] > vDist[j]) {
                        double tmp = vDist[i];
                        vDist[i] = vDist[j];
                        vDist[j] = tmp;
                    }
                }
            }
            double avg = 0.0f;
            for (int i = 1; i < sz - 1; i++) {
                avg += vDist[i];
            }
            avgDist = avg / (sz - 2);
        }
        double avgLeftGass = 0.0f;
        sz = (int)(vLeftGass.size());
        if (sz > 3) {
            for (int i = 0; i < sz - 1; i++) {
                for (int j = i + 1; j < sz; j++) {
                    if (vLeftGass[i] > vLeftGass[j]) {
                        double tmp = vLeftGass[i];
                        vLeftGass[i] = vLeftGass[j];
                        vLeftGass[j] = tmp;
                    }
                }
            }
            double avg = 0.0f;
            for (int i = 1; i < sz - 1; i++) {
                avg += vLeftGass[i];
            }
            avgLeftGass = avg / (sz - 2);
        }
        double avgRightGass = 0.0f;
        sz = (int)(vRightGass.size());
        if (sz > 3) {
            for (int i = 0; i < sz - 1; i++) {
                for (int j = i + 1; j < sz; j++) {
                    if (vRightGass[i] > vRightGass[j]) {
                        double tmp = vRightGass[i];
                        vRightGass[i] = vRightGass[j];
                        vRightGass[j] = tmp;
                    }
                }
            }
            double avg = 0.0f;
            for (int i = 1; i < sz - 1; i++) {
                avg += vRightGass[i];
            }
            avgRightGass = avg / (sz - 2);
        }
        szDist = avgDist;
        leftGassDist = avgLeftGass;
        rightGassDist = avgRightGass;
        minEdgeX = xMinResult[3];
        minEdgeY = yMinResult[3];
        maxEdgeX = xMaxResult[3];
        maxEdgeY = yMaxResult[3];
        minGasX = minEdgeX + leftGassDist - 1;
        minGasY = minEdgeY;
        maxGasX = maxEdgeX - rightGassDist + 1;
        maxGasY = maxEdgeY;
        delete[] imgData;
        imgData = nullptr;
    }
    catch (...) {
        delete[] imgData;
        imgData = nullptr;
    }
}
bool CInspectCamera::MeasureResultProc(MeasurePoint* ptResult, int dotNum, int leftThres, int rightRes, int gasThres, double fzDist,
    double& minX, double& minY, double& maxX, double& maxY, double& minGasX, double& minGasY, double& maxGasX, double& maxGasY){
    /* code */
    minX = -1.0f;
    minY = -1.0f;
    maxX = -1.0f;
    maxY = -1.0f;
    minGasX = -1.0f;
    minGasY = -1.0f;
    if (dotNum < 2) return false;
    //1. 首先查找minX, minY
    bool bMinFin = false;
    minX = MAXINT;
    for (int i = 0; i < dotNum; i++) {
        if (fabs(ptResult[i].cy) > leftThres && ptResult[i].x < minX) {
            minX = ptResult[i].x;
            minY = ptResult[i].y;
            bMinFin = true;
        }
    }
    if (!bMinFin) {
        minX = -1.0f;
        minY = -1.0f;
    }
    //2. 查找左右点
    bool bMaxFind = false;
    maxX = -1.0f;
    for (int i = 0; i < dotNum; i++) {
        if (fabs(ptResult[i].cy) > rightRes && ptResult[i].x > maxX) {
            maxX = ptResult[i].x;
            maxY = ptResult[i].y;
            bMaxFind = true;
        }
    }
    if (!bMaxFind) {
        maxX = -1.0f;
        maxY = -1.0f;
    }
    //3. 查找最小气体点
    if (bMinFin) {
        bool bFind = false;
        minGasX = MAXINT;
        for (int i = 0; i < dotNum; i++) {
            double dx = ptResult[i].x - minX;
            double dy = ptResult[i].y - minY;
            double dist = sqrt(dx * dx + dy * dy);
            if (dist > fzDist) {
                if (fabs(ptResult[i].cy) > gasThres && ptResult[i].x < minGasX && ptResult[i].x > minX) {
                    minGasX = ptResult[i].x;
                    minGasY = ptResult[i].y;
                    bFind = true;
                }
            }
        }
        if (!bFind) {
            minGasX = -1.0f;
            minGasY = -1.0f;
        }
    }
    //4. 查找最大气体点
    if (bMaxFind) {
        bool bFind = false;
        maxGasX = -1.0f;
        for (int i = 0; i < dotNum; i++) {
            double dx = ptResult[i].x - maxX;
            double dy = ptResult[i].y - maxY;
            double dist = sqrt(dx * dx + dy * dy);
            if (dist > fzDist) {
                if (fabs(ptResult[i].cy) > gasThres && ptResult[i].x > maxGasX && ptResult[i].x < maxX) {
                    maxGasX = ptResult[i].x;
                    maxGasY = ptResult[i].y;
                    bFind = true;
                }
            }
        }
        if (!bFind) {
            maxGasX = -1.0f;
            maxGasY = -1.0f;
        }
    }
    return true;
}
int CInspectCamera::CutImageROI(uchar* img, DimensionDir eDim, int x1, int y1, int x2, int y2) {
    /* code */
    int iScan = m_pHardparm->GetScanToDimension(eDim);
    int width = x2 - x1 + 1;
    for (int i = y1; i <= y2; i++) {
        LPBYTE lp =    m_pGrabber->GetFrameHeaderLine(iScan, i);
        std::memcpy(img+ (i-y1) * width, lp+x1, width);
    }
    return 1;
}
BOOL CInspectCamera::CheckThreadEnd(int iThread, stFrameIndex stFrame)
@@ -6259,6 +6711,11 @@
        // 1. Find End Line.. (Corner를 제외한 Side라인을 찾기 위해 End Line부터 찾는다)
        FindEndLine(iThread, emDim, stFrame);
        bool bIsRipSide = (emDim == DIMENSION_A_RIP || emDim == DIMENSION_B_RIP || emDim == DIMENSION_C_RIP || emDim == DIMENSION_D_RIP);
        if (bIsRipSide) {
            return TRUE;
        }
        // 2. Find Side Line.. (해당 프레임에서 Corner/Notch 를 제외한 Side 라인 찾기)
#if USE_AI_DETECT
        FindSideLine(iThread, emDim, stFrame);
EdgeInspector_App/Process/InspectCamera.h
@@ -139,6 +139,18 @@
    //13. 视觉功能
    void FinallyVisionProc(DimensionDir eDim);
    //14. 截图图像
    int CutImageROI(uchar* img, DimensionDir eDim, int x1, int y1, int x2, int y2);
    BOOL MeasureSideEdge_EI(DimensionDir eDim);
    void MeasureSideEdge_POS(DimensionDir eDim,
        int x1, int y1, int x2, int y2,
        int leftThres, int rightThres, int gassThres,
        double& minEdgeX, double& minEdgeY, double& maxEdgeX, double& maxEdgeY,
        double& minGasX, double& minGasY, double& maxGasX, double& maxGasY,
        double& szDist, double& leftGassDist, double& rightGassDist);
    bool MeasureResultProc(MeasurePoint* ptResult, int dotNum, int leftThres, int rightRes, int gasThres, double fzDist,
        double& minX, double& minY, double& maxX, double& maxY, double& minGasX, double& minGasY, double& maxGasX, double& maxGasY);
protected:    
    void                SaveFullImageCopy(int iScan);
EdgeInspector_App/Recipe/GlassRecipe.cpp
@@ -553,6 +553,8 @@
    m_nUserDefectAreaCount = 0;
    for(int i=0; i<MAX_SIDE_USER_DEFECT_AREA_COUNT; i++)
        m_UserDefectPrm[i].Reset();
    m_RipThk.Reset();
}
BOOL CSIDE_PARM::ReadRecipe(CConfig *pFile, int nSideIdx)
@@ -723,6 +725,8 @@
    for(int nUserDefectAreaIdx=0; nUserDefectAreaIdx<m_nUserDefectAreaCount; nUserDefectAreaIdx++)
        m_UserDefectPrm[nUserDefectAreaIdx].ReadRecipe(pFile, nSideIdx, nUserDefectAreaIdx);
    m_RipThk.ReadRecipe(pFile, nSideIdx);
    return TRUE;
}
@@ -891,6 +895,8 @@
    for(int nUserDefectAreaIdx=0; nUserDefectAreaIdx<m_nUserDefectAreaCount; nUserDefectAreaIdx++)
        m_UserDefectPrm[nUserDefectAreaIdx].WriteRecipe(pFile, nSideIdx, nUserDefectAreaIdx);
    m_RipThk.WriteRecipe(pFile, nSideIdx);
    return TRUE;
}
@@ -1705,3 +1711,106 @@
    return TRUE;
}
void CRIP_THICKNESS_PARM::Reset()
{
    m_bUseInspect = FALSE;
    m_nLeftThres = m_nRightThres = m_nGlassThres = 0;
    m_nRipDir = 0;
    m_nRectLeft = m_nRectRight = 0;
    m_nTopOffset = m_nBottomOffset = 0;
    m_nGlassSizeMin_um = 0;  m_nGlassSizeMax_um = 0;
    m_nLeftRipSizeMin_um = 0; m_nLeftRipSizeMax_um = 0;
    m_nRightRipSizeMin_um = 0; m_nRightRipSizeMax_um = 0;
}
BOOL CRIP_THICKNESS_PARM::ReadRecipe(CConfig* pFile, int nSideIdx)
{
    if (!pFile) {
        return FALSE;
    }
    if (nSideIdx < 0 || nSideIdx >= MAX_SIDE_COUNT) {
        return FALSE;
    }
    CString k;
    k = _T("RipThk_m_bUseInspect");
    pFile->GetItemValue(nSideIdx, k, m_bUseInspect, 0);
    k = _T("RipThk_m_nLeftThres");
    pFile->GetItemValue(nSideIdx, k, m_nLeftThres, 0);
    k = _T("RipThk_m_nRightThres");
    pFile->GetItemValue(nSideIdx, k, m_nRightThres, 0);
    k = _T("RipThk_m_nGlassThres");
    pFile->GetItemValue(nSideIdx, k, m_nGlassThres, 0);
    k = _T("RipThk_m_nRipDir");
    pFile->GetItemValue(nSideIdx, k, m_nRipDir, 2);
    k = _T("RipThk_m_nRectLeft");
    pFile->GetItemValue(nSideIdx, k, m_nRectLeft, 0);
    k = _T("RipThk_m_nTopOffset");
    pFile->GetItemValue(nSideIdx, k, m_nTopOffset, 0);
    k = _T("RipThk_m_nRectRight");
    pFile->GetItemValue(nSideIdx, k, m_nRectRight, 0);
    k = _T("RipThk_m_nBottomOffset");
    pFile->GetItemValue(nSideIdx, k, m_nBottomOffset, 0);
    k = _T("RipThk_m_nGlassSizeMin_um");
    pFile->GetItemValue(nSideIdx, k, m_nGlassSizeMin_um, 0);
    k = _T("RipThk_m_nGlassSizeMax_um");
    pFile->GetItemValue(nSideIdx, k, m_nGlassSizeMax_um, 0);
    k = _T("RipThk_m_nLeftRipSizeMin_um");
    pFile->GetItemValue(nSideIdx, k, m_nLeftRipSizeMin_um, 0);
    k = _T("RipThk_m_nLeftRipSizeMax_um");
    pFile->GetItemValue(nSideIdx, k, m_nLeftRipSizeMax_um, 0);
    k = _T("RipThk_m_nRightRipSizeMin_um");
    pFile->GetItemValue(nSideIdx, k, m_nRightRipSizeMin_um, 0);
    k = _T("RipThk_m_nRightRipSizeMax_um");
    pFile->GetItemValue(nSideIdx, k, m_nRightRipSizeMax_um, 0);
    return TRUE;
}
BOOL CRIP_THICKNESS_PARM::WriteRecipe(CConfig* pFile, int nSideIdx)
{
    if (!pFile) {
        return FALSE;
    }
    if (nSideIdx < 0 || nSideIdx >= MAX_SIDE_COUNT) {
        return FALSE;
    }
    CString k;
    k = _T("RipThk_m_bUseInspect");
    pFile->SetItemValue(nSideIdx, k, m_bUseInspect);
    k = _T("RipThk_m_nLeftThres");
    pFile->SetItemValue(nSideIdx, k, m_nLeftThres);
    k = _T("RipThk_m_nRightThres");
    pFile->SetItemValue(nSideIdx, k, m_nRightThres);
    k = _T("RipThk_m_nGlassThres");
    pFile->SetItemValue(nSideIdx, k, m_nGlassThres);
    k = _T("RipThk_m_nRipDir");
    pFile->SetItemValue(nSideIdx, k, m_nRipDir);
    k = _T("RipThk_m_nRectLeft");
    pFile->SetItemValue(nSideIdx, k, m_nRectLeft);
    k = _T("RipThk_m_nTopOffset");
    pFile->SetItemValue(nSideIdx, k, m_nTopOffset);
    k = _T("RipThk_m_nRectRight");
    pFile->SetItemValue(nSideIdx, k, m_nRectRight);
    k = _T("RipThk_m_nBottomOffset");
    pFile->SetItemValue(nSideIdx, k, m_nBottomOffset);
    k = _T("RipThk_m_nGlassSizeMin_um");
    pFile->SetItemValue(nSideIdx, k, m_nGlassSizeMin_um);
    k = _T("RipThk_m_nGlassSizeMax_um");
    pFile->SetItemValue(nSideIdx, k, m_nGlassSizeMax_um);
    k = _T("RipThk_m_nLeftRipSizeMin_um");
    pFile->SetItemValue(nSideIdx, k, m_nLeftRipSizeMin_um);
    k = _T("RipThk_m_nLeftRipSizeMax_um");
    pFile->SetItemValue(nSideIdx, k, m_nLeftRipSizeMax_um);
    k = _T("RipThk_m_nRightRipSizeMin_um");
    pFile->SetItemValue(nSideIdx, k, m_nRightRipSizeMin_um);
    k = _T("RipThk_m_nRightRipSizeMax_um");
    pFile->SetItemValue(nSideIdx, k, m_nRightRipSizeMax_um);
    return TRUE;
}
EdgeInspector_App/Recipe/GlassRecipe.h
@@ -318,6 +318,33 @@
    int        m_nJudge_Size_Min_OR_AND;    // 0 : No Use, 1 : OR, 2 : AND
};
// RIP Thickness
struct CRIP_THICKNESS_PARM
{
    // Inspect
    BOOL m_bUseInspect = FALSE;
    int  m_nLeftThres = 0;
    int  m_nRightThres = 0;
    int  m_nGlassThres = 0;
    int  m_nRipDir = 0;                // 0:L 1:R 2:L+R
    int  m_nRectLeft = 0;            // Inspect Rect L
    int  m_nTopOffset = 0;            // Inspect Top Offset
    int  m_nRectRight = 0;            // Inspect Rect R
    int  m_nBottomOffset = 0;       // Inspect Bottom Offset
    // Judgement
    int  m_nGlassSizeMin_um = 0;
    int  m_nGlassSizeMax_um = 0;
    int  m_nLeftRipSizeMin_um = 0;
    int  m_nLeftRipSizeMax_um = 0;
    int  m_nRightRipSizeMin_um = 0;
    int  m_nRightRipSizeMax_um = 0;
    void Reset();
    BOOL ReadRecipe(CConfig* pFile, int nSideIdx);
    BOOL WriteRecipe(CConfig* pFile, int nSideIdx);
};
class CSIDE_PARM
{    
public:
@@ -453,6 +480,9 @@
    // User Defect
    int                        m_nUserDefectAreaCount;
    CUSER_DEFECT_AREA_PARM    m_UserDefectPrm[MAX_SIDE_USER_DEFECT_AREA_COUNT];
    // Rip Thickness
    CRIP_THICKNESS_PARM        m_RipThk;
};
class CGlassRecipe
EdgeInspector_App/View/ViewMain_ScanImage.cpp
@@ -945,7 +945,7 @@
BOOL CViewMain_ScanImage::ManualThreadStart(int nCmd,int nIndex /*= -1*/)
{
    if(m_pManualProcess) {
        DWORD dwWait = ::WaitForSingleObject(m_pManualProcess->m_hThread, 1000);
        DWORD dwWait = ::WaitForSingleObject(m_pManualProcess->m_hThread, 3000);
        if (dwWait == WAIT_TIMEOUT) {
            DWORD dwExitCode;
            if ((::GetExitCodeThread(m_pManualProcess->m_hThread, &dwExitCode)) && (dwExitCode == STILL_ACTIVE)) {
EdgeInspector_App/ViewMain_Recipe.cpp
@@ -182,6 +182,7 @@
    ON_EVENT(CViewMain_Recipe, IDC_RDO_INSTYPE_IN_CHAMFER, DISPID_CLICK, CViewMain_Recipe::ClickRdoInsType, VTS_NONE)
    ON_EVENT(CViewMain_Recipe, IDC_RDO_INSTYPE_TOP_CORNER, DISPID_CLICK, CViewMain_Recipe::ClickRdoInsType, VTS_NONE)
    ON_EVENT(CViewMain_Recipe, IDC_RDO_INSTYPE_BOT_CORNER, DISPID_CLICK, CViewMain_Recipe::ClickRdoInsType, VTS_NONE)
    ON_EVENT(CViewMain_Recipe, IDC_RDO_INSTYPE_RIP_THICKNESS, DISPID_CLICK, CViewMain_Recipe::ClickRdoInsType, VTS_NONE)
    ON_EVENT(CViewMain_Recipe, IDC_BTN_TOP_MARK_UPDATE_0, DISPID_CLICK, CViewMain_Recipe::ClickBtnMarkUpdate, VTS_NONE)
    ON_EVENT(CViewMain_Recipe, IDC_BTN_TOP_MARK_UPDATE_1, DISPID_CLICK, CViewMain_Recipe::ClickBtnMarkUpdate, VTS_NONE)
@@ -1199,15 +1200,21 @@
        break;
    case IDC_RDO_INSTYPE_BOT_CORNER:    eSelInsType = eRcp_InsType_BotCorner;
        break;
    case IDC_RDO_INSTYPE_RIP_THICKNESS:    eSelInsType = eRcp_InsType_RipThickness;
        break;
    default:
        return;
    }
    if(m_eSelectInsType == eSelInsType)
    // Check Ins Type
    if (m_eSelectInsType == eSelInsType) {
        return;
    }
    // Change Ins Type
    m_eSelectInsType = eSelInsType;
    
    // Update UI
    Init_SideInsInfo();
    Fill_SideInsInfo(FALSE);
@@ -1649,6 +1656,9 @@
    case eRcp_InsType_BotCorner :
        nRows = 14;
        break;
    case eRcp_InsType_RipThickness:
        nRows = 10;
        break;
    default:
        break;
    }
@@ -1825,6 +1835,47 @@
            m_SideInspectInfo.SetItemText(nRowIdx++, nColIdx, vecParams[i]);
        }
    }
    else if (m_eSelectInsType == eRcp_InsType_RipThickness) {
        std::vector<CString> vecParams;
        strTemp.Format(_T("%s Use (0:OFF/1:ON)"), g_strInsType[m_eSelectInsType]);
        vecParams.push_back(strTemp);
        strTemp.Format(_T("%s Ins. Left Thres"), g_strInsType[m_eSelectInsType]);
        vecParams.push_back(strTemp);
        strTemp.Format(_T("%s Ins. Right Thres"), g_strInsType[m_eSelectInsType]);
        vecParams.push_back(strTemp);
        strTemp.Format(_T("%s Glsss Thres"), g_strInsType[m_eSelectInsType]);
        vecParams.push_back(strTemp);
        strTemp.Format(_T("%s Rip Dir. (0:Left/1:Right/2:Both)"), g_strInsType[m_eSelectInsType]);
        vecParams.push_back(strTemp);
        strTemp.Format(_T("%s Ins. Rect L"), g_strInsType[m_eSelectInsType]);
        vecParams.push_back(strTemp);
        strTemp.Format(_T("%s Ins. T Offset"), g_strInsType[m_eSelectInsType]);
        vecParams.push_back(strTemp);
        strTemp.Format(_T("%s Ins. Rect R"), g_strInsType[m_eSelectInsType]);
        vecParams.push_back(strTemp);
        strTemp.Format(_T("%s Ins. B Offset"), g_strInsType[m_eSelectInsType]);
        vecParams.push_back(strTemp);
        CLanguageControl* pLanguageControl = g_pStatus->m_pLanguageControl;
        if (pLanguageControl != NULL) {
            for (int i = 0; i < vecParams.size(); i++) {
                CString strParam = pLanguageControl->GetString(m_eSelectLangType, vecParams[i]);
                if (strParam.IsEmpty() || strParam.GetLength() == 0) {
                    continue;
                }
                vecParams[i] = strParam;
            }
        }
        for (int i = 0; i < vecParams.size(); i++) {
            m_SideInspectInfo.GetCell(nRowIdx, nColIdx)->SetState(GVIS_READONLY);
            m_SideInspectInfo.SetRowHeight(nRowIdx, 19);
            m_SideInspectInfo.SetItemText(nRowIdx++, nColIdx, vecParams[i]);
        }
    }
    m_SideInspectInfo.SetFixedRowSelection(FALSE);
    m_SideInspectInfo.SetFixedColumnSelection(FALSE);
@@ -1858,6 +1909,9 @@
    case eRcp_InsType_TopCorner :
    case eRcp_InsType_BotCorner :
        nRows = 11;
        break;
    case eRcp_InsType_RipThickness:
        nRows = 7;
        break;
    default:
        break;
@@ -2008,6 +2062,40 @@
        for(int i=0; i<vecParams.size(); i++)
        {
            m_JudgementInfo.GetCell(nRowIdx, nColIdx)->SetState(GVIS_READONLY);
            m_JudgementInfo.SetRowHeight(nRowIdx, 19);
            m_JudgementInfo.SetItemText(nRowIdx++, nColIdx, vecParams[i]);
        }
    }
    else if (eRcp_InsType_RipThickness == m_eSelectInsType) {
        std::vector<CString> vecParams;
        strTemp.Format(_T("Glass Size Min um"), g_strInsType[m_eSelectInsType]);
        vecParams.push_back(strTemp);
        strTemp.Format(_T("Glass Size Max um"), g_strInsType[m_eSelectInsType]);
        vecParams.push_back(strTemp);
        strTemp.Format(_T("Left Rip Size Min um"), g_strInsType[m_eSelectInsType]);
        vecParams.push_back(strTemp);
        strTemp.Format(_T("Left Rip Size Max um"), g_strInsType[m_eSelectInsType]);
        vecParams.push_back(strTemp);
        strTemp.Format(_T("Right Rip Size Min um"), g_strInsType[m_eSelectInsType]);
        vecParams.push_back(strTemp);
        strTemp.Format(_T("Right Rip Size Max um"), g_strInsType[m_eSelectInsType]);
        vecParams.push_back(strTemp);
        CLanguageControl* pLanguageControl = g_pStatus->m_pLanguageControl;
        if (pLanguageControl != NULL) {
            for (int i = 0; i < vecParams.size(); i++) {
                CString strParam = pLanguageControl->GetString(m_eSelectLangType, vecParams[i]);
                if (strParam.IsEmpty() || strParam.GetLength() == 0)
                    continue;
                vecParams[i] = strParam;
            }
        }
        for (int i = 0; i < vecParams.size(); i++) {
            m_JudgementInfo.GetCell(nRowIdx, nColIdx)->SetState(GVIS_READONLY);
            m_JudgementInfo.SetRowHeight(nRowIdx, 19);
            m_JudgementInfo.SetItemText(nRowIdx++, nColIdx, vecParams[i]);
@@ -2471,6 +2559,55 @@
                strTemp.Format(_T("%d"), pSideParam->m_nBottomCornerEdgeThreshold );
                m_SideInspectInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
            }
            else if (m_eSelectInsType == eRcp_InsType_RipThickness)
            {
                CRIP_THICKNESS_PARM& rip = pSideParam->m_RipThk;
                // 1. Use (0/1)
                if (rip.m_bUseInspect)  m_SideInspectInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(RGB(50, 255, 50));
                else                    m_SideInspectInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(RGB(255, 50, 50));
                strTemp.Format(_T("%d"), rip.m_bUseInspect);
                m_SideInspectInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
                // 2. Left Thres
                m_SideInspectInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(uCellColor);
                strTemp.Format(_T("%d"), rip.m_nLeftThres);
                m_SideInspectInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
                // 3. Right Thres
                m_SideInspectInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(uCellColor);
                strTemp.Format(_T("%d"), rip.m_nRightThres);
                m_SideInspectInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
                // 4. Glass Thres
                m_SideInspectInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(uCellColor);
                strTemp.Format(_T("%d"), rip.m_nGlassThres);
                m_SideInspectInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
                // 5. Rip Direction (0/1/2)
                m_SideInspectInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(uCellColor);
                strTemp.Format(_T("%d"), rip.m_nRipDir);
                m_SideInspectInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
                // 6. Rect L
                m_SideInspectInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(uCellColor);
                strTemp.Format(_T("%d"), rip.m_nRectLeft);
                m_SideInspectInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
                // 7. T Offset
                m_SideInspectInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(uCellColor);
                strTemp.Format(_T("%d"), rip.m_nTopOffset);
                m_SideInspectInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
                // 8. Rect R
                m_SideInspectInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(uCellColor);
                strTemp.Format(_T("%d"), rip.m_nRectRight);
                m_SideInspectInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
                // 9. B Offset
                m_SideInspectInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(uCellColor);
                strTemp.Format(_T("%d"), rip.m_nBottomOffset);
                m_SideInspectInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
            }
        }
        m_SideInspectInfo.Invalidate();
@@ -2639,10 +2776,49 @@
                strTemp = m_SideInspectInfo.GetItemText(nRowIdx++, nColIdx);
                pSideParam->m_nBottomCornerEdgeThreshold  = _wtoi(strTemp);
            }
            else if (m_eSelectInsType == eRcp_InsType_RipThickness)
            {
                CRIP_THICKNESS_PARM& rip = pSideParam->m_RipThk;
                // 1. Use
                strTemp = m_SideInspectInfo.GetItemText(nRowIdx++, nColIdx);
                rip.m_bUseInspect = _wtoi(strTemp);
                // 2. Left Thres
                strTemp = m_SideInspectInfo.GetItemText(nRowIdx++, nColIdx);
                rip.m_nLeftThres = _wtoi(strTemp);
                // 3. Right Thres
                strTemp = m_SideInspectInfo.GetItemText(nRowIdx++, nColIdx);
                rip.m_nRightThres = _wtoi(strTemp);
                // 4. Glass Thres
                strTemp = m_SideInspectInfo.GetItemText(nRowIdx++, nColIdx);
                rip.m_nGlassThres = _wtoi(strTemp);
                // 5. Rip Direction
                strTemp = m_SideInspectInfo.GetItemText(nRowIdx++, nColIdx);
                rip.m_nRipDir = _wtoi(strTemp);
                // 6. Rect L
                strTemp = m_SideInspectInfo.GetItemText(nRowIdx++, nColIdx);
                rip.m_nRectLeft = _wtoi(strTemp);
                // 7. T Offset
                strTemp = m_SideInspectInfo.GetItemText(nRowIdx++, nColIdx);
                rip.m_nTopOffset = _wtoi(strTemp);
                // 8. Rect R
                strTemp = m_SideInspectInfo.GetItemText(nRowIdx++, nColIdx);
                rip.m_nRectRight = _wtoi(strTemp);
                // 9. B Offset
                strTemp = m_SideInspectInfo.GetItemText(nRowIdx++, nColIdx);
                rip.m_nBottomOffset = _wtoi(strTemp);
            }
        }
    }
}
void CViewMain_Recipe::Fill_JudgementInfo(BOOL bGetData)
{
@@ -2791,6 +2967,40 @@
                strTemp.Format(_T("%d"), pSideParam->m_nBottomCorner_Measure_Judge_OR_AND );
                m_JudgementInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
            }
            else if (m_eSelectInsType == eRcp_InsType_RipThickness)
            {
                const CRIP_THICKNESS_PARM& rip = pSideParam->m_RipThk;
                // 1. Glass Size Min um
                m_JudgementInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(uCellColor);
                strTemp.Format(_T("%d"), rip.m_nGlassSizeMin_um);
                m_JudgementInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
                // 2. Glass Size Max um
                m_JudgementInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(uCellColor);
                strTemp.Format(_T("%d"), rip.m_nGlassSizeMax_um);
                m_JudgementInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
                // 3. Left Rip Size Min um
                m_JudgementInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(uCellColor);
                strTemp.Format(_T("%d"), rip.m_nLeftRipSizeMin_um);
                m_JudgementInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
                // 4. Left Rip Size Max um
                m_JudgementInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(uCellColor);
                strTemp.Format(_T("%d"), rip.m_nLeftRipSizeMax_um);
                m_JudgementInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
                // 5. Right Rip Size Min um
                m_JudgementInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(uCellColor);
                strTemp.Format(_T("%d"), rip.m_nRightRipSizeMin_um);
                m_JudgementInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
                // 6. Right Rip Size Max um
                m_JudgementInfo.GetCell(nRowIdx, nColIdx)->SetBackClr(uCellColor);
                strTemp.Format(_T("%d"), rip.m_nRightRipSizeMax_um);
                m_JudgementInfo.SetItemText(nRowIdx++, nColIdx, strTemp);
            }
        }
        m_JudgementInfo.Invalidate();
@@ -2907,6 +3117,34 @@
                strTemp = m_JudgementInfo.GetItemText(nRowIdx++, nColIdx);
                pSideParam->m_nBottomCorner_Measure_Judge_OR_AND  = _wtoi(strTemp);
            }
            else if (m_eSelectInsType == eRcp_InsType_RipThickness)
            {
                CRIP_THICKNESS_PARM& rip = pSideParam->m_RipThk;
                // 1. Glass Size Min um
                strTemp = m_JudgementInfo.GetItemText(nRowIdx++, nColIdx);
                rip.m_nGlassSizeMin_um = _wtoi(strTemp);
                // 2. Glass Size Max um
                strTemp = m_JudgementInfo.GetItemText(nRowIdx++, nColIdx);
                rip.m_nGlassSizeMax_um = _wtoi(strTemp);
                // 3. Left Rip Size Min um
                strTemp = m_JudgementInfo.GetItemText(nRowIdx++, nColIdx);
                rip.m_nLeftRipSizeMin_um = _wtoi(strTemp);
                // 4. Left Rip Size Max um
                strTemp = m_JudgementInfo.GetItemText(nRowIdx++, nColIdx);
                rip.m_nLeftRipSizeMax_um = _wtoi(strTemp);
                // 5. Right Rip Size Min um
                strTemp = m_JudgementInfo.GetItemText(nRowIdx++, nColIdx);
                rip.m_nRightRipSizeMin_um = _wtoi(strTemp);
                // 6. Right Rip Size Max um
                strTemp = m_JudgementInfo.GetItemText(nRowIdx++, nColIdx);
                rip.m_nRightRipSizeMax_um = _wtoi(strTemp);
            }
        }
    }
}
@@ -2939,11 +3177,9 @@
{
    UINT nID = GetFocus()->GetDlgCtrlID();
    SetSideRadioExclusive(nID);
    UpdateRecipe(TRUE);
    ChangViewCdmSide(nID);
    RefreshRadioStates();
}
@@ -2951,10 +3187,10 @@
{
    UINT nID = GetFocus()->GetDlgCtrlID();
    Fill_SideInsInfo(TRUE);    // Get Data..
    Fill_SideInsInfo(TRUE);
    Fill_JudgementInfo(TRUE);
    ChangInsTypeSide(nID);
    RefreshRadioStates();
}
void CViewMain_Recipe::ClickButtonLang()
@@ -3260,17 +3496,69 @@
#endif // HALCON_VISION_KEY
}
void CViewMain_Recipe::SetSideRadioExclusive(UINT nSelId)
void CViewMain_Recipe::RefreshRadioStates()
{
    static const UINT kIds[] = {
    // 内部映射 lambda:枚举 -> 控件 ID
    auto InsTypeToCtrlId = [](eViewCmdInsType t) -> UINT {
        switch (t) {
        case eRcp_InsType_Chip:          return IDC_RDO_INSTYPE_CHIP;
        case eRcp_InsType_Crack:         return IDC_RDO_INSTYPE_CRACK;
        case eRcp_InsType_Burr:          return IDC_RDO_INSTYPE_BURR;
        case eRcp_InsType_Chamfer:       return IDC_RDO_INSTYPE_CHAMFER;
        case eRcp_InsType_In_Chip:       return IDC_RDO_INSTYPE_IN_CHIP;
        case eRcp_InsType_In_Crack:      return IDC_RDO_INSTYPE_IN_CRACK;
        case eRcp_InsType_In_Burr:       return IDC_RDO_INSTYPE_IN_BURR;
        case eRcp_InsType_In_Chamfer:    return IDC_RDO_INSTYPE_IN_CHAMFER;
        case eRcp_InsType_TopCorner:     return IDC_RDO_INSTYPE_TOP_CORNER;
        case eRcp_InsType_BotCorner:     return IDC_RDO_INSTYPE_BOT_CORNER;
        case eRcp_InsType_RipThickness:  return IDC_RDO_INSTYPE_RIP_THICKNESS;
        default: return 0;
        }
    };
    auto SideToCtrlId = [](eViewCmdSide s) -> UINT {
        switch (s) {
        case eRcp_SideRD_A:      return IDC_RDO_SIDE_A_TOP;
        case eRcp_SideRD_B:      return IDC_RDO_SIDE_B_TOP;
        case eRcp_SideRD_C:      return IDC_RDO_SIDE_C_TOP;
        case eRcp_SideRD_D:      return IDC_RDO_SIDE_D_TOP;
        case eRcp_SideRD_A_DN:   return IDC_RDO_SIDE_A_BOT;
        case eRcp_SideRD_B_DN:   return IDC_RDO_SIDE_B_BOT;
        case eRcp_SideRD_C_DN:   return IDC_RDO_SIDE_C_BOT;
        case eRcp_SideRD_D_DN:   return IDC_RDO_SIDE_D_BOT;
        case eRcp_SideRD_A_RIP:  return IDC_RDO_SIDE_A_RIP;
        case eRcp_SideRD_B_RIP:  return IDC_RDO_SIDE_B_RIP;
        case eRcp_SideRD_C_RIP:  return IDC_RDO_SIDE_C_RIP;
        case eRcp_SideRD_D_RIP:  return IDC_RDO_SIDE_D_RIP;
        default: return 0;
        }
    };
    const UINT nInsIds[] = {
        IDC_RDO_INSTYPE_CHIP, IDC_RDO_INSTYPE_CRACK, IDC_RDO_INSTYPE_BURR,
        IDC_RDO_INSTYPE_CHAMFER, IDC_RDO_INSTYPE_IN_CHIP, IDC_RDO_INSTYPE_IN_CRACK,
        IDC_RDO_INSTYPE_IN_BURR, IDC_RDO_INSTYPE_IN_CHAMFER,
        IDC_RDO_INSTYPE_TOP_CORNER, IDC_RDO_INSTYPE_BOT_CORNER,
        IDC_RDO_INSTYPE_RIP_THICKNESS
    };
    const UINT nSideIds[] = {
        IDC_RDO_SIDE_A_TOP, IDC_RDO_SIDE_B_TOP, IDC_RDO_SIDE_C_TOP, IDC_RDO_SIDE_D_TOP,
        IDC_RDO_SIDE_A_BOT, IDC_RDO_SIDE_B_BOT, IDC_RDO_SIDE_C_BOT, IDC_RDO_SIDE_D_BOT,
        IDC_RDO_SIDE_A_RIP, IDC_RDO_SIDE_B_RIP, IDC_RDO_SIDE_C_RIP, IDC_RDO_SIDE_D_RIP
    };
    for (UINT id : kIds) {
        if (CButton* p = (CButton*)GetDlgItem(id)) {
            p->SetCheck(id == nSelId ? 1 : 0);
    const UINT nSelIns = InsTypeToCtrlId(m_eSelectInsType);
    for (UINT id : nInsIds) {
        if (auto* p = (CButton*)GetDlgItem(id)) {
            p->SetCheck(id == nSelIns ? 1 : 0);
        }
    }
    const UINT nSelSide = SideToCtrlId(m_eSelectSide);
    for (UINT id : nSideIds) {
        if (auto* p = (CButton*)GetDlgItem(id)) {
            p->SetCheck(id == nSelSide ? 1 : 0);
        }
    }
}
EdgeInspector_App/ViewMain_Recipe.h
@@ -81,7 +81,7 @@
    BOOL            WriteLinkFile(CString strFilePath);
    void            ChangeRecipe();
    BOOL            ReadRecipe(CString strRecipe);
    void            SetSideRadioExclusive(UINT nSelId);
    void            RefreshRadioStates();
public:
    void            UpdateRecipe(BOOL bGetData, int type = 0);
EdgeInspector_App/resource.h
@@ -581,26 +581,26 @@
#define IDC_RDO_SIDE_B_BOT              1814
#define IDC_RDO_SIDE_C_BOT              1815
#define IDC_RDO_SIDE_D_BOT              1816
#define IDC_RDO_INSTYPE_CHIPCRACK       1817
#define IDC_RDO_INSTYPE_CHIP            1817
#define IDC_RDO_INSTYPE_BURR            1818
#define IDC_RDO_INSTYPE_BURR2           1819
#define IDC_RDO_INSTYPE_CHAMFER         1819
#define IDC_RDO_INSTYPE_TOP_CORNER      1820
#define IDC_RDO_INSTYPE_BOT_CORNER      1821
#define IDC_RDO_INSTYPE_CRACK           1822
#define IDC_RDO_SIDE_A_RIP              1823
#define IDC_RDO_SIDE_B_RIP              1824
#define IDC_RDO_INSTYPE_CF_CHIPCRACK    1825
#define IDC_RDO_INSTYPE_IN_CHIP         1825
#define IDC_RDO_INSTYPE_CF_BURR         1826
#define IDC_RDO_INSTYPE_IN_BURR         1826
#define IDC_RDO_INSTYPE_IN_CRACK        1827
#define IDC_RDO_INSTYPE_IN_BURR2        1828
#define IDC_RDO_INSTYPE_IN_CHAMFER      1828
#define IDC_RDO_SIDE_C_RIP              1829
#define IDC_RDO_SIDE_D_BOT2             1830
#define IDC_RDO_SIDE_D_RIP              1830
#define IDC_RDO_SIDE_A_RIP              1817
#define IDC_RDO_SIDE_B_RIP              1818
#define IDC_RDO_SIDE_C_RIP              1819
#define IDC_RDO_SIDE_D_RIP              1820
#define IDC_RDO_INSTYPE_CHIPCRACK       1821
#define IDC_RDO_INSTYPE_CHIP            1822
#define IDC_RDO_INSTYPE_BURR            1823
#define IDC_RDO_INSTYPE_BURR2           1824
#define IDC_RDO_INSTYPE_CHAMFER         1825
#define IDC_RDO_INSTYPE_TOP_CORNER      1826
#define IDC_RDO_INSTYPE_BOT_CORNER      1827
#define IDC_RDO_INSTYPE_CRACK           1828
#define IDC_RDO_INSTYPE_CF_CHIPCRACK    1829
#define IDC_RDO_INSTYPE_IN_CHIP         1830
#define IDC_RDO_INSTYPE_CF_BURR         1831
#define IDC_RDO_INSTYPE_IN_BURR         1832
#define IDC_RDO_INSTYPE_IN_CRACK        1833
#define IDC_RDO_INSTYPE_IN_CHAMFER      1834
#define IDC_RDO_INSTYPE_RIP_THICKNESS   1835
#define IDC_RDO_INSTYPE_RIP_THICKNESS2  1860
#define ID_PROFILE_CHECKALL             32782
#define ID_PROFILE_CLEARALL             32783
#define ID_PROFILE_CURRENTX             32784