#include "cprocess_level.h"
#include "logic.h"
#include "caffinetrans.h"
#include "cmworktable.h"
#include "cworktablelaseroffset.h"
#include "cworktableoffsetdriver.h"
CProcess_Level::CProcess_Level(const QString& name, int32_t id)
    :CMProcess(name, id)
{

    m_Variants.add_Variant(Var_Process_Level_MaxLevelCount,
                           QObject::trUtf8("MaxLevelCount"), int(5), false,
                           Variant::TOBOTH);

    m_Variants.add_Variant(Var_Process_Level_MaxSearchCount,
                           QObject::trUtf8("MaxSearchCount"), int(6), false,
                           Variant::TOBOTH);

    m_Variants.add_Variant(Var_Process_Level_OffsetThres,
                           QObject::trUtf8("OffsetThres"), double(0.0015), false,
                           Variant::TOBOTH, QStringList(), "mm");

    m_Variants.add_Variant(Var_Process_Level_AngleThres,
                           QObject::trUtf8("AngleThres"), double(0.2), false,
                           Variant::TOBOTH, QStringList(), QObject::tr("degree"));
    m_Variants.add_Variant(Var_Process_Level_AngleOffset,
                           QObject::trUtf8("AngleOffset"), double(0), false,
                           Variant::TOBOTH, QStringList(), QObject::tr("degree"));

    m_Variants.add_Variant(Var_Process_Level_ROIParent,
                           QObject::trUtf8("ROIParent"), double(0), false,
                           Variant::TOBOTH);
    m_Variants.add_Variant(Var_Process_Level_LevelParent,
                           QObject::trUtf8("LevelParent"), double(0), false,
                           Variant::TOBOTH);

    m_Variants.add_Variant(Var_Process_Level_MidStepOffset,
                           QObject::trUtf8("MidStepOffset"), int(3), false,
                           Variant::TOBOTH);
    m_Variants.add_Variant(Var_Process_Level_MidOffsetThres,
                           QObject::trUtf8("MidOffsetThres"), double(0.015), false,
                           Variant::TOBOTH, QStringList(), QObject::tr("mm"));

    m_Variants.add_Variant(Var_Process_Level_BaseOffsetMax,
                           QObject::trUtf8("BaseOffsetMax"), double(0.005), false,
                           Variant::TOBOTH, QStringList(), QObject::tr("mm"));
    m_Variants.add_Variant(Var_Process_Level_BaseOffsetMin,
                           QObject::trUtf8("BaseOffsetMin"), double(0.001), false,
                           Variant::TOBOTH, QStringList(), QObject::tr("mm"));

    m_Variants.add_Variant(Var_Process_Level_ModelTestCount,
                           QObject::trUtf8("ModelTestCount"), int(9), false,
                           Variant::TOXML);
    m_Variants.add_Variant(Var_Process_Level_BeforeSearchSleep,
                           QObject::trUtf8("BeforeSearchSleep"), int(200), false,
                           Variant::TOXML, QStringList(), QObject::tr("ms"));
    m_Variants.add_Variant(Var_Process_Level_AfterSearchSleep,
                           QObject::trUtf8("AfterSearchSleep"), int(200), false,
                           Variant::TOXML, QStringList(), QObject::tr("ms"));
    m_Variants.add_Variant(Var_Process_Level_EnableAutoFoucs,
                           QObject::trUtf8("EnableAutoFoucs"), false, false,
                           Variant::TOXML);


    m_Variants.add_Variant(Var_Process_Align_AFirst,
                           QObject::trUtf8("AlignAFirst"), int(0), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_Align_AStep,
                           QObject::trUtf8("AlignAStep"), int(0), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_Align_BFirst,
                           QObject::trUtf8("AlignBFirst"), int(0), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_Align_BStep,
                           QObject::trUtf8("AlignBStep"), int(0), false,
                           Variant::TOFILE);

    m_Variants.add_Variant(Var_ProductSide_TemplateOffsetSideALowX,
                           QObject::trUtf8("LowOffsetX"), double(0), false,
                           Variant::TOFILE, QStringList(), QObject::tr("mm"));
    m_Variants.add_Variant(Var_ProductSide_TemplateOffsetSideALowY,
                           QObject::trUtf8("LowOffsetY"), double(0), false,
                           Variant::TOFILE, QStringList(), QObject::tr("mm"));
    m_Variants.add_Variant(Var_ProductSide_TemplateOffsetSideBLowX,
                           QObject::trUtf8("LowOffsetX"), double(0), false,
                           Variant::TOFILE, QStringList(), QObject::tr("mm"));
    m_Variants.add_Variant(Var_ProductSide_TemplateOffsetSideBLowY,
                           QObject::trUtf8("LowOffsetY"), double(0), false,
                           Variant::TOFILE, QStringList(), QObject::tr("mm"));
    m_Variants.add_Variant(Var_Process_LevelParamDouble_Reserved5,
                           QObject::trUtf8("ParamDoubleReserved"), double(0), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_LevelParamDouble_Reserved6,
                           QObject::trUtf8("ParamDoubleReserved"), double(0), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_LevelParamDouble_Reserved7,
                           QObject::trUtf8("ParamDoubleReserved"), double(0), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_LevelParamDouble_Reserved8,
                           QObject::trUtf8("ParamDoubleReserved"), double(0), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_LevelParamDouble_Reserved9,
                           QObject::trUtf8("ParamDoubleReserved"), double(0), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_LevelParamDouble_Reserved10,
                           QObject::trUtf8("ParamDoubleReserved"), double(0), false,
                           Variant::TOFILE);

    m_Variants.add_Variant(Var_Process_ProductSide_CutStartA,
                           QObject::trUtf8("CutStartA"), int(0), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_ProductSide_CutEndA,
                           QObject::trUtf8("CutEndA"), int(0), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_ProductSide_CutStartB,
                           QObject::trUtf8("CutStartB"), int(0), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_ProductSide_CutEndB,
                           QObject::trUtf8("CutEndB"), int(0), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_ModelEnableLowOffset,
                           QObject::trUtf8("EnableModelLowOffset"), bool(false), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_Enable_Align_Check,
                           QObject::trUtf8("EnableAlignCheck"), bool(false), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_EnableHalfcut_Check,
                           QObject::trUtf8("EnableHalfCutCheck"), bool(false), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_LevelParamInt_Reserved8,
                           QObject::trUtf8("ParamIntReserved"), int(0), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_LevelParamInt_Reserved9,
                           QObject::trUtf8("ParamIntReserved"), int(0), false,
                           Variant::TOFILE);
    m_Variants.add_Variant(Var_Process_LevelParamInt_Reserved10,
                           QObject::trUtf8("ParamIntReserved"), int(0), false,
                           Variant::TOFILE);

    m_iLocateCamID = CAM2X;
    m_iMatchCamID = CAM6X;

    m_imageContainerAlign[0][0].SetName(QObject::trUtf8("Side A PT1"));
    m_imageContainerAlign[0][1].SetName(QObject::trUtf8("Side A PT2"));

    m_imageContainerAlign[1][0].SetName(QObject::trUtf8("Side B PT1"));
    m_imageContainerAlign[1][1].SetName(QObject::trUtf8("Side B PT2"));

    m_imageContainerBase[0].SetName(QObject::trUtf8("Side A PT Base"));
    m_imageContainerBase[1].SetName(QObject::trUtf8("Side B PT Base"));

    m_iCurLineId = -1;
}

CProcess_Level::~CProcess_Level()
{

}


void CProcess_Level::do_msg(int iID, QVariantList& vlInput,
                            V_MsgOutput* pvlOutput)
{
    int32_t isize = vlInput.size();
    switch (iID) {
    case CHANGE_TO_LEVEL_SIDE:
        if (isize >= 1) {
            ChangeToLevelSide(vlInput[0].toInt());
        }
        break;
    case CALCU_PT_BY_CELL:
        if (isize >= 1) {
            CalcuPtByCell(vlInput[0].toInt());
        }
        break;
    case CALCU_PT_BY_LINE:
        if (isize >= 2) {
            CalcuPtByLine(vlInput[0].toInt(), vlInput[1].toInt());
        }
        break;
    case CALCU_PT_BY_POS:
        if (isize >= 5) {
            CalcuPtByPos(vlInput[0].toInt(),
                         vlInput[1].toDouble(), vlInput[2].toDouble(),
                         vlInput[3].toDouble(), vlInput[4].toDouble());
        }
        break;
    case MOVE_PT:
        if (isize >= 6) {
            MovePt(vlInput[0].toInt(), vlInput[1].toInt(), vlInput[2].toInt(),
                   vlInput[3].toBool(), vlInput[4].toInt(), vlInput[5].toInt());
        }
        break;
    case SEARCH_ROTATE:
        if (isize >= 2) {
            SearchRotatePt(vlInput[0].toInt(), vlInput[1].toInt());
        }
        break;
    case SEARCH_ALIGN:
        if (isize >= 3) {
            SearchAlignPt(vlInput[0].toInt(), vlInput[1].toInt(),
                          vlInput[2].toInt());
        }
        break;
    case SEARCH_BASE:
        if (isize >= 3) {
            SearchAlignPt(vlInput[0].toInt(), vlInput[1].toInt(),
                          vlInput[2].toInt());
        }
        break;
    case TURN_ANGLE:
        if (isize >= 2) {
            Turn_Angle(vlInput[0].toInt(), vlInput[1].toInt());
        }
        break;
    case SETPT_MANUAL:
        if (isize >= 4) {
            SetPtManual(vlInput[0].toInt(), vlInput[1].toInt(),
                        vlInput[2].toInt(), vlInput[3].toInt());
        }
        break;
    case TEST_MODEL:
        if (isize >= 2) {
            Test_Model(vlInput[0].toInt(), vlInput[1].toInt());
        }
        break;
    case FINDMODEL_ONCE:
        if (isize >= 2) {
            FindModelOnce(vlInput[0].toInt(), vlInput[1].toInt());
        }
        break;
    case SETLEVELLIGHT:
        if (isize >= 2) {
            SetLevelLight(vlInput[0].toInt(), vlInput[1].toInt());
        }
        break;
    case LOADLEVELMODEL:
        if (isize >= 1) {
            LoadLevelModel(vlInput[0].toString());
        }
        break;
    case LOADCALIBRATIONMODEL:
        if (isize >= 1) {
            LoadCalibrationModel(vlInput[0].toString());
        }
        break;
    }
    CProcess::do_msg(iID, vlInput, pvlOutput);
}

#if 0
void CProcess_Level::Serialize(QDataStream& stream, bool bwrite, int iVersion)
{
    QMap<int, Variant>::Iterator iter = m_Variants.m_mVariantMap.begin();
    for (; iter != m_Variants.m_mVariantMap.end(); iter++) {
        int valueType = (*iter).m_iSerialStyle;
        if (valueType == Variant::TOFILE || valueType == Variant::TOBOTH) {
            if (bwrite) {
                //#ifndef __DEFINE_NEW_RESERVED_PARAM__
                //				if((*iter).m_iID >= Var_Process_LevelParamDouble_Reserved1 
                //					&& (*iter).m_iID <= Var_Process_LevelParamInt_Reserved10)
                //				{
                //					continue;
                //				}
                //#endif
                stream << (*iter).m_vVar;
            } else {
#ifdef __DEFINE_NEW_RESERVED_PARAM__
                if ((*iter).m_iID >= Var_Process_LevelParamDouble_Reserved1
                    && (*iter).m_iID <= Var_Process_LevelParamInt_Reserved10) {
                    continue;
                }
#endif
#ifdef __DEFINE_NEW_PARAM__
                if ((*iter).m_iID == Var_Process_Level_MidOffsetThres)
                    continue;

                if ((*iter).m_iID == Var_Process_Level_AngleOffset)
                    continue;

                int id = (*iter).m_iID;
                if (id == Var_Process_Align_AFirst
                    || id == Var_Process_Align_AStep
                    || id == Var_Process_Align_BFirst
                    || id == Var_Process_Align_BStep) {
                    continue;
                }
#endif // __DEFINE_NEW_PARAM__
                QVariant var;
                stream >> var;
                (*iter).m_vVar = var;
            }
        }
    }
}
#endif

void CProcess_Level::ClearContainer()
{
    m_imageContainerAlign[0][0].ClearImage();
    m_imageContainerAlign[0][1].ClearImage();
    m_imageContainerAlign[1][0].ClearImage();
    m_imageContainerAlign[1][1].ClearImage();
    m_imageContainerBase[0].ClearImage();
    m_imageContainerBase[1].ClearImage();
}

ModelData* CProcess_Level::GetModelData(int iSideID, int iCamID)
{
    int iSideIndex = iSideID == SIDE_A ? 0 : 1;
    int iCamIndex = iCamID == CAM2X ? 0 : 1;
    return &m_ModelData[iSideIndex][iCamIndex];
}

//加载调平的模型
void CProcess_Level::LoadLevelModel(QString& sFilePath)
{
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            int iCamID = i == 0 ? CAM2X : CAM6X;
            int iSideID = j == 0 ? SIDE_A : SIDE_B;

            CCam* pCam = GET_CHDIRECT(iCamID, CCam);
            Q_ASSERT(pCam);
            CAMatch* pMatch = (CAMatch*)pCam->Find_Algorithm(m_gSystem.m_iMatchType);
            Q_ASSERT(pMatch);
            try {
                pMatch->loadModel(m_gSystem.makeModelPath(sFilePath, iSideID, iCamID),
                                  GetModelData(iSideID, iCamID));
            } catch (QString& s) {
                MqDebug() << s;
            }
        }
    }

    CProductSide* pSide = GET_CHDIRECT(SIDE_A, CProductSide);
    Q_ASSERT(pSide);
    pSide->m_bModelChanged = true;
    pSide = GET_CHDIRECT(SIDE_B, CProductSide);
    Q_ASSERT(pSide);
    pSide->m_bModelChanged = true;
}

void CProcess_Level::LoadCalibrationModel(QString& sFilePath)
{
    CCam* pCam = GET_CHDIRECT(CAM6X, CCam);
    Q_ASSERT(pCam);
    CAMatch* pMatch = (CAMatch*)pCam->Find_Algorithm(M_CALIBRATION_NCC);
    Q_ASSERT(pMatch);
    try {
        QString folderPath = CMakePath::make_pattern_path("Calibration");
        QString filePath = QString("%1/%2").arg(folderPath).arg("Cam6X.mnm");
        pMatch->loadModel(filePath, &m_CalibrationModelData[0]);
    } catch (QString& s) {
        MqDebug() << s;
    }
}

void CProcess_Level::ChangeToLevelSide(int iSideID)
{
    m_gSystem.ChangeSide(iSideID);
}

//计算左 中 右 基准 旋转5个点
int32_t CProcess_Level::calcuPt(int32_t iSideID,
                                double baseposX,
                                double baseposY,
                                double dStartPos,
                                double dEndPos)
{
    double dLevelParent = m_Variants.get_VData(Var_Process_Level_LevelParent)
        ->toDouble();
    //步长，使用另一面的步长
    double dStep = getOtherSideStep(iSideID);

    //划线长度
    double dLength = fabs(dEndPos - dStartPos) * dLevelParent / 2;
    dLength = int(dLength / dStep) * dStep;

    //中点偏移比例
    double dMidStepOffset = m_Variants.get_VData(Var_Process_Level_MidStepOffset)
        ->toInt();
    CMPointd& LevelPt1 = m_AlignPt1.Pt;
    CMPointd& LevelPt2 = m_AlignPt2.Pt;
    CMPointd& LevelPtMid = m_AlignPtMid.Pt;
    CMPointd& LinePt = m_AlignPtBase.Pt;
    CMPointd& RotatePt = m_AlignPtRotate.Pt;

    LevelPt1.tY = LevelPt2.tY = LevelPtMid.tY = LinePt.tY = baseposY;
    LinePt.tX = baseposX;
    LevelPt1.tX = baseposX - dLength;
    LevelPt2.tX = baseposX + dLength;
    LevelPtMid.tX = baseposX - dLength + dStep * dMidStepOffset;
    //RotatePt=LinePt;
    RotatePt = LevelPt1;
    return MSG_FINISHED;
}

//根据晶圆计算切割道
void CProcess_Level::CalcuPtByCell(int iSideID)
{
    //    CProductSide* pSide=GET_CHDIRECT(iSideID,CProductSide);
    //    Q_ASSERT(pSide);
    double dBasePosX = 0;
    double dBasePosY = 0;
    double dStart = 0;
    double dEnd = 0;
    double dLeft, dRight, dBottom, dTop;
    getCircleBoundary(iSideID, dLeft, dRight, dBottom, dTop);
    dBasePosY = (dBottom + dTop) / 2;
    getX(iSideID, dBasePosY, dStart, dEnd);

    dBasePosX = (dStart + dEnd) / 2;
    calcuPt(iSideID, dBasePosX, dBasePosY, dStart, dEnd);
    m_iCurLineId = -1;
}

void CProcess_Level::CalcuPtByLine(int iSideID, int iLineID)
{
    //sideID A or B
    CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
    Q_ASSERT(pSide);
    double dBasePosX = 0;
    double dBasePosY = 0;
    double dStart = 0;
    double dEnd = 0;
    CUTLINE* pCutLine = pSide->GetCutLine(iLineID);
    Q_ASSERT(pCutLine);
    dStart = pCutLine->m_WPPair.ptNegative.tX;
    dEnd = pCutLine->m_WPPair.ptPostive.tX;

    dBasePosX = pCutLine->m_BasePt.tX;
    dBasePosY = pCutLine->m_BasePt.tY;

    m_iCurLineId = iLineID;

    calcuPt(iSideID, dBasePosX, dBasePosY, dStart, dEnd);
}

void CProcess_Level::CalcuPtByPos(int iSideID, double baseposX, double baseposY, double dStartPos, double dEndPos)
{
    double dLevelParent = m_Variants.get_VData(Var_Process_Level_LevelParent)
        ->toDouble();
    double dStep = getOtherSideStep(iSideID);

    double dLength = fabs(dEndPos - dStartPos) * dLevelParent / 2;
    dLength = int(dLength / dStep) * dStep;

    double dMidStepOffset = m_Variants.get_VData(Var_Process_Level_MidStepOffset)
        ->toInt();

    CMPointd& LevelPt1 = m_AlignPt1.Pt;
    CMPointd& LevelPt2 = m_AlignPt2.Pt;
    CMPointd& LevelPtMid = m_AlignPtMid.Pt;
    CMPointd& LinePt = m_AlignPtBase.Pt;
    CMPointd& RotatePt = m_AlignPtRotate.Pt;


    LevelPt1.tY = LevelPt2.tY = LevelPtMid.tY = LinePt.tY = baseposY;
    LinePt.tX = baseposX;
    LevelPt1.tX = baseposX - dLength;
    LevelPt2.tX = baseposX + dLength;
    LevelPtMid.tX = baseposX - dLength + dStep * dMidStepOffset;
    RotatePt = LinePt;
}

//移动到指定点
void CProcess_Level::MovePt(int iSideID, int iCamID, int ptstyle, bool adjustToTemplatePt,
                            int iXAdjust, int iYAdjust)
{
    //MqDebug()<<"Move Pt";
    bool bStep = ptstyle == TYPE_PTROTATE ? false : true;
    double xAdjustStep = get_XAdjust_step(iSideID, iCamID, bStep);
    //double yAdjustStep= get_YAdjust_step( iSideID,  iCamID,bStep);
    double xoffset = iXAdjust * xAdjustStep;

    double yAdjustStep = get_YAdjust_step(iSideID, iCamID, bStep);
    double yoffset = iYAdjust * yAdjustStep;

    CMPointd wt;
    switch (ptstyle) {
    case TYPE_PT1: wt = m_AlignPt1.Pt; break; //点1
    case TYPE_PTMID: wt = m_AlignPtMid.Pt; break; //中点
    case TYPE_PT2: wt = m_AlignPt2.Pt; break; //点2
    case TYPE_PTBASE: wt = m_AlignPtBase.Pt; break;    //基准点
    case TYPE_PTROTATE: wt = m_AlignPtRotate.Pt; break;    //旋转点
    }
    wt.tX += xoffset;
    wt.tY += yoffset;
    if (adjustToTemplatePt) {
        if (iCamID == CAM2X) {

            CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
            Q_ASSERT(pSide);
            double dX = pSide->m_Variants.get_VData(Var_ProductSide_TemplateOffsetX)
                ->toDouble();
            double dY = pSide->m_Variants.get_VData(Var_ProductSide_TemplateOffsetY)
                ->toDouble();
            bool bEnableLowOffset = m_Variants.get_VData(Var_Process_ModelEnableLowOffset)->toInt() > 0.1;
            if (iSideID == SIDE_A && bEnableLowOffset) {
                dX = m_Variants.get_VData(Var_ProductSide_TemplateOffsetSideALowX)->toDouble();
                dY = m_Variants.get_VData(Var_ProductSide_TemplateOffsetSideALowY)->toDouble();
            } else if (iSideID == SIDE_B && bEnableLowOffset) {
                dX = m_Variants.get_VData(Var_ProductSide_TemplateOffsetSideBLowX)->toDouble();
                dY = m_Variants.get_VData(Var_ProductSide_TemplateOffsetSideBLowY)->toDouble();
            }

            wt.tX += dX;
            wt.tY += dY;

        } else {
            CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
            Q_ASSERT(pSide);
            wt.tX += pSide->m_Variants.get_VData(Var_ProductSide_TemplateOffsetX)
                ->toDouble();
            wt.tY += pSide->m_Variants.get_VData(Var_ProductSide_TemplateOffsetY)
                ->toDouble();
        }
    }
    posMove(iSideID, wt, iCamID);
}

//找模板匹配点
bool CProcess_Level::search_pt(int iSideID, CMPointd& WTResult, double& dAngle,
                               int iCamID, bool benableROI, CImageContainer* pContainer,
                               bool adjustToRefPt)
{
    SetLevelLight(iSideID, iCamID);
    SetCamParam(iSideID, iCamID);

    MSleep(m_Variants.get_VData(Var_Process_Level_BeforeSearchSleep)
           ->toInt());

    CMImage Image;
    CCam* pCam = GET_CHDIRECT(iCamID, CCam);
    Q_ASSERT(pCam);
    CAMatch* pMatch = (CAMatch*)pCam->Find_Algorithm(m_gSystem.m_iMatchType);
    Q_ASSERT(pMatch);
    CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
    Q_ASSERT(pSide);
    CMWorkTableDriver* pWorkTableDriver = GET_CARD(WORKTABLE_DRIVER, CMWorkTableDriver);
    Q_ASSERT(pWorkTableDriver);

    ModelData* pModelData = GetModelData(iSideID, iCamID);
    Q_ASSERT(pModelData);
    if (pModelData->iModelID == -1) {
        MQT_THROW_CODESTR(ALARM_MAIN_LOGIC_PARAM, QObject::trUtf8("%1 %2 has not make pattern")
                          .arg(iSideID == SIDE_A ? "A_SIDE" : "B_SIDE")
                          .arg(m_gSystem.m_mapNameList[iCamID]));
    }
    pMatch->m_Variants.get_VData(Var_Algorithm_EnableROI_ID)->setValue(benableROI);

    MODEL_INFO& info = pSide->get_Model_info(iCamID);
    //double dscore = pMatch->m_Variants.get_VData(Var_Match_ThresScore_ID)->toDouble();
    pMatch->m_Variants.get_VData(Var_Match_ThresScore_ID)->setValue(info.dMinScore);
    //pMatch->m_Variants.get_VData(Var_Match_ThresScore_ID)->setValue(0.5);
    //pMatch->m_Variants.get_VData(Var_Match_NumMatches_ID)->setValue(3);

    int  width, height;
    pCam->GetPixelSize(width, height);
    pMatch->m_rect_ROI.m_center = pCam->get_camImageCenter();

    if (benableROI) {
        double roirate = m_Variants.get_VData(Var_Process_Level_ROIParent)
            ->toDouble();
        //        double dStepY= getCurSideStep(iSideID);
        //        double dStepX= getOtherSideStep(iSideID);

        //        CMPointd pt= pCam->LSizeToDSize(CMPointd(dStepX,dStepY));

        //        pMatch->m_rect_ROI.m_height=pt.tY*roirate;

        //        pMatch->m_rect_ROI.m_width=pt.tX*roirate;

        //        pMatch->m_rect_ROI.m_height=min(height-10,
        //                                        pMatch->m_rect_ROI.m_height);
        //        pMatch->m_rect_ROI.m_width=min(width-10,
        //                                        pMatch->m_rect_ROI.m_width);

        //        pMatch->m_rect_ROI.m_height=max(pModelData->ModelImage.m_height+2,
        //                                        pMatch->m_rect_ROI.m_height);
        //        pMatch->m_rect_ROI.m_width=max(pModelData->ModelImage.m_width*1.2+2,
        //                                        pMatch->m_rect_ROI.m_width);

        //pMatch->m_rect_ROI.m_height=pModelData->ModelImage.m_height+2;
        //pMatch->m_rect_ROI.m_width=pModelData->ModelImage.m_width*roirate+2;

        if (roirate > 0.98) roirate = 0.8;
        if (roirate <= 0.1) roirate = 0.1;

        pMatch->m_rect_ROI.m_height = height * roirate;
        pMatch->m_rect_ROI.m_width = width * roirate;
    } else {
        pMatch->m_rect_ROI.m_height = height * 0.9;
        pMatch->m_rect_ROI.m_width = width * 0.8;
    }

    CCamDriver* pCamDriver = GET_CARD(CAM_DRIVER, CCamDriver);
    Q_ASSERT(pCamDriver);


    pCamDriver->grab_image(iCamID, &Image);
    if (pContainer) {
        pMatch->showROI(pContainer);
    }
    pMatch->setModelData(pModelData);
    //
    pMatch->exe(&Image);
    pMatch->showresult(&pCam->m_imageContainer);
    if (pContainer) {
        FreshImage(iCamID, Image, pContainer, "", true);
        pMatch->showresult_shape(pContainer);
    }

    if (!pMatch->lcenters.size()) {
        //        QString filepath=CMakePath::make_file_path(QString("search_failed.bmp"));
        //        CAlgorithm::save_image_to_File(filepath,pImage );
        MSleep(m_Variants.get_VData(Var_Process_Level_AfterSearchSleep)
               ->toInt());
        return false;
    }
    CMPointd PixelResult;
    int32_t idx;
    CMPointd ptCenter = pCam->get_camImageCenter();
    pMatch->get_mostnear_point(PixelResult, idx, ptCenter);
    MqDebug() << "Search Pt:X " << PixelResult.tX << "Y" << PixelResult.tY << endl;
    //PixelResult返回值应该是以Center为基准的偏移
    pWorkTableDriver->CurCamToWt(iCamID, PixelResult, WTResult, true);
    if (adjustToRefPt) {
        //m_iLocateCamID
        if (iCamID == CAM2X) {
            CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
            Q_ASSERT(pSide);
            double dX = pSide->m_Variants.get_VData(Var_ProductSide_TemplateOffsetX)
                ->toDouble();
            double dY = pSide->m_Variants.get_VData(Var_ProductSide_TemplateOffsetY)
                ->toDouble();
            bool bEnableLowOffset = m_Variants.get_VData(Var_Process_ModelEnableLowOffset)->toInt() > 0.1;
            if (iSideID == SIDE_A && bEnableLowOffset) {
                dX = m_Variants.get_VData(Var_ProductSide_TemplateOffsetSideALowX)->toDouble();
                dY = m_Variants.get_VData(Var_ProductSide_TemplateOffsetSideALowY)->toDouble();
            } else if (iSideID == SIDE_B && bEnableLowOffset) {
                dX = m_Variants.get_VData(Var_ProductSide_TemplateOffsetSideBLowX)->toDouble();
                dY = m_Variants.get_VData(Var_ProductSide_TemplateOffsetSideBLowY)->toDouble();
            }

            WTResult.tX -= dX;
            WTResult.tY -= dY;

        } else {
            WTResult.tX -=
                pSide->m_Variants.get_VData(Var_ProductSide_TemplateOffsetX)
                ->toDouble();
            WTResult.tY -=
                pSide->m_Variants.get_VData(Var_ProductSide_TemplateOffsetY)
                ->toDouble();
        }
    }
    dAngle = pMatch->lresults[idx].angle;
    double dScore = pMatch->lresults[idx].score;
    if (pContainer) {
        pContainer->addString(QString("A:%1 Q:%2").arg(QString::number(dAngle, 'f', 2)).arg(QString::number(dScore, 'f', 2)));
    }

    MSleep(m_Variants.get_VData(Var_Process_Level_AfterSearchSleep)
           ->toInt());
    return true;
}
void CProcess_Level::SearchRotatePt(int iSideID, int iCamID)
{
    AlignPoint* pALignPt = 0;
    CImageContainer* pContainer = 0;
    pALignPt = &m_AlignPtRotate;
    int index = iSideID == SIDE_A ? 0 : 1;
    pContainer = &(m_imageContainerBase[index]);

    MSleep(200);
    //CaptureImage(iCamID,pContainer,QObject::trUtf8("Rotate Align"));

    CMPointd pt;
    pALignPt->bHasMatch = search_pt(iSideID, pt, pALignPt->dAngle,
                                    iCamID, false,
                                    pContainer,
                                    true);

    if (pALignPt->bHasMatch) {
        double dAngleOffset = m_Variants.get_VData(Var_Process_Level_AngleOffset)->toDouble();
        pALignPt->dAngle += dAngleOffset;
        QString str = QObject::trUtf8("Angle:%1").arg(QString::number(pALignPt->dAngle, 'f', 3));
        m_AlignPtRotate.Pt = pt;
        pContainer->addString(str);
        //CaptureImage(iCamID,pContainer,str,false);
        adjustPt(iSideID, TYPE_PTROTATE);
    }
}
double CProcess_Level::SearchAlignPt(int iSideID, int iCamID, int ptstyle)
{
    AlignPoint* pALignPt = 0;
    CImageContainer* pContainer = 0;
    int index = iSideID == SIDE_A ? 0 : 1;
    switch (ptstyle) {
    case TYPE_PT1:
        pALignPt = &m_AlignPt1;
        pContainer = &(m_imageContainerAlign[index][0]);
        pContainer->addString(QObject::trUtf8("Align Pt1"));
        //CaptureImage(iCamID,pContainer,QObject::trUtf8("Align Pt1"));
        break;
    case TYPE_PTMID:
        pALignPt = &m_AlignPtMid;
        pContainer = &(m_imageContainerAlign[index][1]);
        pContainer->addString(QObject::trUtf8("Align PtMid"));
        //CaptureImage(iCamID,pContainer,QObject::trUtf8("Align PtMid"));
        break;
    case TYPE_PT2:
        pALignPt = &m_AlignPt2;
        pContainer = &(m_imageContainerAlign[index][1]);
        pContainer->addString(QObject::trUtf8("Align Pt2"));
        //CaptureImage(iCamID,pContainer,QObject::trUtf8("Align Pt2"));
        break;
    default:Q_ASSERT(0); break;
    }
    CMPointd pt;
    pALignPt->bHasMatch = search_pt(iSideID, pt, pALignPt->dAngle,
                                    iCamID, true, pContainer,
                                    true);
    double dOffset = 0;
    if (pALignPt->bHasMatch) {
        dOffset = pt.tY - pALignPt->Pt.tY;

        QString str = QObject::trUtf8("offset:%1mm").arg(QString::number(dOffset, 'f', 4));
        CaptureImage(iCamID, pContainer, str, false);

        CCam* pCam = GET_CHDIRECT(iCamID, CCam);
        Q_ASSERT(pCam);
        CAMatch* pMatch = (CAMatch*)pCam->Find_Algorithm(m_gSystem.m_iMatchType);
        Q_ASSERT(pMatch);
        pMatch->showresult_shape(pContainer);

        pALignPt->Pt = pt;

        adjustPt(iSideID, ptstyle);
    }
    return dOffset;
}

void CProcess_Level::SearchBasePt(int iSideID, int iCamID, int iLineID)
{
    AlignPoint* pALignPt = 0;
    CImageContainer* pContainer = 0;
    pALignPt = &m_AlignPtBase;
    int index = iSideID == SIDE_A ? 0 : 1;
    pContainer = &(m_imageContainerBase[index]);
    //CaptureImage(iCamID,pContainer,QObject::trUtf8("Align PtBase"));
    CMPointd pt;
    pALignPt->bHasMatch = search_pt(iSideID, pt, pALignPt->dAngle,
                                    iCamID, true, pContainer, true);
    if (pALignPt->bHasMatch) {
        double dOffset = pt.tY - pALignPt->Pt.tY;
        double dOffsetMax = m_Variants.get_VData(Var_Process_Level_BaseOffsetMax)->toDouble();
        double dOffsetMin = m_Variants.get_VData(Var_Process_Level_BaseOffsetMin)->toDouble();

        CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
        Q_ASSERT(pSide);
        int iLineSize = pSide->m_CutLineVector.size();
        if (fabs(dOffset) > dOffsetMax && iLineSize) {
            MQT_THROW_CODESTR(ALARM_MAIN_ALIGNPT_BaseLineOutrange, QObject::trUtf8("find base line err."
                                                                                   " offset(%1mm) out of range(%2mm).")
                              .arg(dOffset, 0, 'f', 4).arg(dOffsetMax));
        }

        if (fabs(dOffset) > dOffsetMin && !iLineSize) {
            if (fabs(dOffset) > 3 * dOffsetMin) {
                LOG_ERR(fmt::format("Align Base outrange:{}", dOffset));
                QString info = (QObject::tr("Align err.\n") + QObject::trUtf8("find base line "
                                                                              " offset(%1mm) out of range(%2mm).")
                                .arg(dOffset, 0, 'f', 4).arg(dOffsetMin))
                    + "\n" + QObject::tr("Please contact engineer");
                MQT_THROW_CODESTR(ALARM_MAIN_ALIGNPT_AlignBaseLineOutrange, info);
            } else {
                MQT_THROW_CODESTR(ALARM_MAIN_ALIGNPT_AlignBaseLineOutrange, (QObject::tr("Align err.\n") + QObject::trUtf8("find base line "
                                                                                                                           " offset(%1mm) out of range(%2mm).")
                                                                             .arg(dOffset, 0, 'f', 4).arg(dOffsetMin)));
            }
        }

        pALignPt->Pt = pt;
        //LOG(INFO)<<"Match Base Pt Move Pt to (" << pt.tX << "," << pt.tY << ")";
        pContainer->clearShape(-1);
        MovePt(iSideID, iCamID, TYPE_PTBASE, false, 0, 0);

        MSleep(50);
        CaptureImage(iCamID, pContainer,
                     QObject::trUtf8("Offset:%1mm").arg(dOffset, 0, 'f', 4), false);
        m_gSystem.SetCutStreet(pContainer, iSideID, iCamID);

        //int iLineSize=pSide->m_CutLineVector.size();
        if (fabs(dOffset) > 0.001 || !iLineSize) {
            //找到基准点后，就修正切割道
            calcuSideVector(iSideID, dOffset, iLineID);
        } else {
            //LOG(INFO) << "Base Align(Auto) " << iLineID+1 << " Not Modify Offset:" << dOffset;
            LOG_WARN(fmt::format("Ignore calcuSideVector offset:{} LineID:{} ", dOffset, iLineID + 1));
            m_gSystem.LogInfo(QString("Base Align(Auto) ") + QString::number(iLineID + 1) + " Not Modify Offset:" + QString::number(dOffset));
            AddProductData(QString("Base Align(Auto) ") + QString::number(iLineID + 1) + " Not Modify Offset:" + QString::number(dOffset));
        }
    }
}

void CProcess_Level::CaptureImage(int iCamID, CImageContainer* pImageContainer,
                                  QString str, bool bclearShape)
{
    MSleep(m_Variants.get_VData(Var_Process_Level_AfterSearchSleep)
           ->toInt());
    CCamDriver* pCamDriver = GET_CARD(CAM_DRIVER, CCamDriver);
    Q_ASSERT(pCamDriver);
    CMImage img;
    pCamDriver->grab_image(iCamID, &img);
    pImageContainer->SetImage(img, true);
    if (bclearShape) {
        pImageContainer->clearShape(-1);
        pImageContainer->clearString();
    }
    pImageContainer->addString(str);

}


void CProcess_Level::FreshImage(int iCamID, CMImage& img, CImageContainer* pImageContainer, QString str, bool bclearShape/*=true*/)
{
    CCamDriver* pCamDriver = GET_CARD(CAM_DRIVER, CCamDriver);
    Q_ASSERT(pCamDriver);
    pImageContainer->SetImage(img, true);
    if (bclearShape) {
        pImageContainer->clearShape(-1);
        pImageContainer->clearString();
    }
    if (!str.isEmpty())
        pImageContainer->addString(str);
}

void CProcess_Level::calcuSideVector(int32_t iSideID,
                                     double dYOffset,
                                     int iLineID, bool bManualSet/*=false*/)
{
    CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
    Q_ASSERT(pSide);
    int iLineSize = pSide->m_CutLineVector.size();
    if (iLineSize) {
        if (fabs(dYOffset) < 0.001) {
            /*LOG(WARNING)<<"Ignore calcuSideVector offset:"<<dYOffset<<"LineID:"<<iLineID+1<<"Manual:"<<bManualSet;*/
            QString info = QString("Align Base Align(Auto) LINE(Ignored):") + QString::number(iLineID + 1) + " Offset" + QString::number(dYOffset) + (SIDE_A == iSideID ? " CH1" : " CH2");;
            AddProductData(info);

            m_gSystem.EvtLog(QString::fromUtf8("POS"),
                             QObject::trUtf8("%3,LineID:%2,Offset:%1,Ignore").
                             arg(dYOffset).arg(iLineID + 1).arg(iSideID == SIDE_A ? "CH1" : "CH2"));
            return;
        }
        iLineID = max(0, iLineID);

        dYOffset = dYOffset / 2;
        if (dYOffset > 0) {
            dYOffset -= 0.0003;
            if (dYOffset < 0) {
                dYOffset = 0;
            }
        }
        if (dYOffset < 0) {
            dYOffset += 0.0003;
            if (dYOffset > 0) {
                dYOffset = 0;
            }
        }
        //更新基准点
        int iFirstLine;
        int iStep;
        getCurSideAlignParam(iSideID, iFirstLine, iStep);

#if 1
        //如果是第一条，则往后更新
        if (iFirstLine == iLineID + 1) {
            iLineID = 0;
        }
        //直接向后更新,第一条
        for (int i = iLineID; i < iLineSize; i++) {
            pSide->m_CutLineVector[i].m_WPPair.ptPostive.tY += dYOffset;
            pSide->m_CutLineVector[i].m_WPPair.ptNegative.tY += dYOffset;
            pSide->m_CutLineVector[i].m_BasePt.tY += dYOffset;
        }

        if (bManualSet) {
            //LOG(INFO) << INFO_TITTLE()<< m_sName.toStdString() <<" Base Align(Manual) LINE:"<< iLineID+1 << " Offset" << dYOffset;
            QString info = QString("Align Base Align(Manual) LINE:") + QString::number(iLineID + 1) + " Offset" + QString::number(dYOffset) + (SIDE_A == iSideID ? " CH1" : " CH2");
            AddProductData(info);
            //m_gSystem.LogInfo(info);
        } else {
            //LOG(INFO) << INFO_TITTLE()<< m_sName.toStdString() <<" Base Align(Auto) LINE:"<< iLineID+1 << " Offset" << dYOffset;
            QString info = QString("Align Base Align(Auto) LINE:") + QString::number(iLineID + 1) + " Offset" + QString::number(dYOffset) + (SIDE_A == iSideID ? " CH1" : " CH2");
            AddProductData(info);
            //m_gSystem.LogInfo(info);
        }
#endif
#if 0
        //向后更新
        if (bManualSet && iLineID <= 0) {
            //手动时，直接向后更新
            for (int i = iLineID; i < iLineSize; i++) {
                pSide->m_CutLineVector[i].m_WPPair.first.tY += dYOffset;
                pSide->m_CutLineVector[i].m_WPPair.second.tY += dYOffset;
                pSide->m_CutLineVector[i].m_BasePt.tY += dYOffset;

                LOG(INFO) << "Base Align(Manual)" << "Total " << iLineSize << " Start:" << iLineID + 1 << "End" << iLineSize << " Offset" << dYOffset;
            }
        } else {
            //第0次更新的时候，直接所有数据都更新
            //如果没启用更新则向后更新
            //如果没有范围，则也向后更新
            //更新时如果是范围更新，则两次更新的时，不能更新到同一条线上，如果重复更新就错了
            //例如：Step= 3  first = 3 后面依次为6 9 12
            //更新 6 时，向前更新4 向后更新 7  更新9 时向前更新 8 向后更新10
            //例如：Step= 4  first = 3 后面依次为7 11 15
            //更新 7 时，向前更新5,6 向后更新 8  更新11 时向前更新 9,10 向后更新12
            //如上保证了不会重复更新
            int iRangeMin = iLineID;
            int iRangeMax = iLineSize;
            if (iStep >= 2 && iLineID > 0 && iFirstLine > 0) {
                iStep = iStep - 1;
                int iRangeUp = iStep % 2 == 0 ? iStep / 2 : iStep / 2 + 1;
                int iRangeDown = iStep / 2;
                iRangeMin = max(0, iLineID - iRangeUp);

                //如果是更新FirstLine，则从第一条线开始更新
                if (iLineID == iFirstLine - 1) {
                    iRangeMin = 0;
                }

                iRangeMax = min(iLineID + iRangeDown, iLineSize - 1);
                //如果后面超出了有效范围，则直接更新后面的所有
                if (iLineID + iStep > iLineSize - iFirstLine - 1) {
                    iRangeMax = iLineSize - 1;
                }
            }

            //iRangeMax = min(iRangeMax, iLineSize-1);

            MqDebug() << "----Base Align " << "Total " << iLineSize << " Line" << iLineID + 1 << " Start:" << iRangeMin + 1 << " End" << iRangeMax + 1;
            if (bManualSet)
                LOG(INFO) << "Base Align(Manual) " << "Total " << iLineSize << " Line" << iLineID + 1 << " Start:" << iRangeMin + 1 << " End" << iRangeMax + 1 << " Offset" << dYOffset;
            else
                LOG(INFO) << "Base Align(Auto) " << "Total " << iLineSize << " Line" << iLineID + 1 << " Start:" << iRangeMin + 1 << " End" << iRangeMax + 1 << " Offset" << dYOffset;
            for (int i = iRangeMin; i <= iRangeMax; i++) {
                pSide->m_CutLineVector[i].m_WPPair.first.tY += dYOffset;
                pSide->m_CutLineVector[i].m_WPPair.second.tY += dYOffset;
                pSide->m_CutLineVector[i].m_BasePt.tY += dYOffset;

            }
        }
#endif
        m_gSystem.EvtLog(QString::fromUtf8("POS"),
                         QObject::trUtf8("%3,LineID:%2,Offset:%1").
                         arg(dYOffset).arg(iLineID + 1).arg(iSideID == SIDE_A ? "CH1" : "CH2"));
    } else {
        AddProductData(QString("Generate Side:%1 Line  at:%2").arg(iSideID == SIDE_A ? "CH1" : "CH2").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz")));
        AddProductData(QString("Base Line Offset:%1").arg(dYOffset));
        //没有的时候，直接生成切割道
        //内部会进行平台误差的补偿
        calcuLineCircle(iSideID);
    }
}

double CProcess_Level::get_XAdjust_step(int iSideID, int iCamID, bool bIndex)
{
    if (bIndex) { return -getOtherSideStep(iSideID); }

    CCam* pCam = GET_CHDIRECT(iCamID, CCam);
    double  width, height;
    pCam->GetRealSize(width, height);
    if (DEQUAL0(width)) {
        MQT_THROW_CODESTR(ALARM_MAIN_LOGIC_PARAM, QObject::trUtf8("level calcu err.X Adjust step equal 0"));
    }
    double dAdjustStep = width / 1.3;
    MqDebug() << "Get_XAdjust_step:" << dAdjustStep;
    return dAdjustStep;
}

//bIndex=false 直接返回die的步长
//
double CProcess_Level::get_YAdjust_step(int iSideID, int iCamID, bool bIndex)
{
    if (bIndex) { return getCurSideStep(iSideID); }

    CCam* pCam = GET_CHDIRECT(iCamID, CCam);
    double  width, height;
    pCam->GetRealSize(width, height);
    if (DEQUAL0(height)) {
        MQT_THROW_CODESTR(ALARM_MAIN_LOGIC_PARAM, QObject::trUtf8("level calcu err.Y Adjust step equal 0"));
    }
    //HACK:1.1的参数根据实际调整或加到Recipe中
    double dAdjustStep = height / 1.2;

    MqDebug() << "Get_YAdjust_step:" << dAdjustStep;
    return dAdjustStep;
}

//查找时调整步进的次数
int CProcess_Level::get_XAdjust_count(int iSideID, int iCamID)
{
    //HACK:1.2这个参数根据实际调整
    int iCount = getOtherSideStep(iSideID) * 1.2 /
        get_XAdjust_step(iSideID, iCamID, false);
    iCount++;
    if (iCount <= 1) {
        iCount = 2;
    }
    MqDebug() << "Get_XAdjust_count:" << iCount;
    return iCount;
}

int CProcess_Level::get_YAdjust_count(int iSideID, int iCamID)
{
    int iCount = getCurSideStep(iSideID) * 1.2 /
        get_YAdjust_step(iSideID, iCamID, false);
    iCount++;
    if (iCount <= 1) {
        iCount = 2;
    }
    MqDebug() << "Get_YAdjust_count:" << iCount;
    return iCount;
}

double CProcess_Level::getCurSideStep(int iSideID)
{
    CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
    Q_ASSERT(pSide);
    double dStep =
        pSide->m_Variants.get_VData(Var_ProductSide_SideStep)
        ->toDouble();
    if (DEQUAL0(dStep)) {
        MQT_THROW_CODESTR(ALARM_MAIN_LOGIC_PARAM, QObject::trUtf8("Side: %1 Step Equal 0. ")
                          .arg(pSide->m_sName));
    }
    return dStep;
}

double CProcess_Level::getOtherSideStep(int iSideID)
{
    Q_ASSERT(iSideID == SIDE_A || iSideID == SIDE_B);
    iSideID = iSideID == SIDE_A ? SIDE_B : SIDE_A;
    return getCurSideStep(iSideID);
}

void CProcess_Level::getCurSideAlignParam(int iSideID, int& iFirst, int& iStep)
{
    Q_ASSERT(iSideID == SIDE_A || iSideID == SIDE_B);
    iFirst = 0;
    iStep = 10;
    if (iSideID == SIDE_A) {
        iFirst = m_Variants.get_VData(Var_Process_Align_AFirst)->toInt();
        iStep = m_Variants.get_VData(Var_Process_Align_AStep)->toInt();
    } else if (iSideID == SIDE_B) {
        iFirst = m_Variants.get_VData(Var_Process_Align_BFirst)->toInt();
        iStep = m_Variants.get_VData(Var_Process_Align_BStep)->toInt();
    }

    if (iFirst < 0) {
        iFirst = 0;
    }
    if (iStep < 0) {
        iStep = abs(iStep);
    }
}

BOOL CProcess_Level::getX(int iSideID, double Y,
                          double& XStart, double& XEnd)
{
    CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
    Q_ASSERT(pSide);
    CMProduct* pProduct = GET_CHDIRECT(PRODUCT, CMProduct);
    Q_ASSERT(pProduct);
    double dLeft = 0, dRight = 0, dBottom = 0, dTop = 0;
    getCircleBoundary(iSideID, dLeft, dRight, dBottom, dTop);
    if (Y > dTop || Y < dBottom) {
        XStart = XEnd = pSide->m_realCenter.tX;
        return FALSE;
    }
    double dRadius = pProduct->m_Variants.get_VData(Var_Product_Diameter_ID)
        ->toDouble() / 2;
    CMPointd CenterPt = pSide->m_realCenter;
    double dYOffset = fabs(Y - CenterPt.tY);
    if (dYOffset > dRadius)dYOffset = dRadius;
    double dXOffset = sqrtf(dRadius * dRadius - dYOffset * dYOffset);
    XStart = CenterPt.tX - dXOffset;
    XEnd = CenterPt.tX + dXOffset;
    if (XStart > dRight) {
        XStart = dRight;
    }
    if (XEnd < dLeft) {
        XEnd = dLeft;
    }
    return 0;
}

void CProcess_Level::getCircleBoundary(int iSideID, double& dLeft,
                                       double& dRight,
                                       double& dBottom,
                                       double& dTop)
{
    CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
    Q_ASSERT(pSide);
    CMProduct* pProduct = GET_CHDIRECT(PRODUCT, CMProduct);
    Q_ASSERT(pProduct);
    double dRadius = 0;
    CMPointd CenterPt;
    dRadius = pProduct->m_Variants.get_VData(Var_Product_Diameter_ID)
        ->toDouble() / 2;
    double  dHalfLine = fabs(pProduct->m_Variants.
                             get_VData(Var_MProduct_FlatLength_ID)->toDouble() / 2);

    //处理平边
    if (dHalfLine >= dRadius) {
        dHalfLine = dRadius * 0.999;
    }
    CenterPt = pSide->m_realCenter;

    double angleStep = DEG(asin(dHalfLine / dRadius)); //平边角度

    dLeft = CenterPt.tX - dRadius;
    dRight = CenterPt.tX + dRadius;
    dBottom = CenterPt.tY - dRadius;    //X向右为正，Y向上为正（即向里）
    dTop = CenterPt.tY + dRadius;
    if (DEQUAL0(angleStep)) {
        //如果没有平边，直接返回
        return;
    }
    int32_t flatPos = pProduct->m_Variants.get_VData(Var_MProduct_FlatPos_ID)
        ->toUInt();
    if (iSideID == SIDE_A) {
        if (flatPos == MYLEFT) { dLeft = CenterPt.tX - dRadius * cos(RAD(angleStep)); }
        if (flatPos == MYTOP) { dTop = CenterPt.tY + dRadius * cos(RAD(angleStep)); }
        if (flatPos == MYBOTTOM) { dBottom = CenterPt.tY - dRadius * cos(RAD(angleStep)); }
        if (flatPos == MYRIGHT) { dRight = CenterPt.tX + dRadius * cos(RAD(angleStep)); }

    } else if (iSideID == SIDE_B) {
        if (flatPos == MYLEFT) { dTop = CenterPt.tY + dRadius * cos(RAD(angleStep)); }
        if (flatPos == MYTOP) { dRight = CenterPt.tX + dRadius * cos(RAD(angleStep)); }
        if (flatPos == MYBOTTOM) { dLeft = CenterPt.tX - dRadius * cos(RAD(angleStep)); }
        if (flatPos == MYRIGHT) { dBottom = CenterPt.tY - dRadius * cos(RAD(angleStep)); }
    } else {
        Q_ASSERT(0);
    }
}

//旋转后重新计算出中心点
int CProcess_Level::turn(int iSideID, double dAngleRel)
{
    CAffineTrans Trans;
    Trans.CalcuParam(CMPointd(0, 0), -dAngleRel, CMPointd(1, 1));
    m_AlignPt1.Pt = Trans.AffineSrctoDest(m_AlignPt1.Pt);
    m_AlignPt2.Pt = Trans.AffineSrctoDest(m_AlignPt2.Pt);
    m_AlignPtMid.Pt = Trans.AffineSrctoDest(m_AlignPtMid.Pt);
    m_AlignPtBase.Pt = Trans.AffineSrctoDest(m_AlignPtBase.Pt);
    m_AlignPtRotate.Pt = Trans.AffineSrctoDest(m_AlignPtRotate.Pt);

    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);

    MSleep(100);
    CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
    Q_ASSERT(pSide);
    pSide->m_dLevelAngle = pDriver->get_posdata(MOTORW, true) + dAngleRel; //TOOD:W轴停止不稳定,要等5秒才停稳
    pDriver->absmove(MOTORW, pSide->m_dLevelAngle - 0.005);
    pDriver->absmove(MOTORW, pSide->m_dLevelAngle);
    //pDriver->relmove(MOTORW,dAngleRel,TRUE);
    MSleep(100);

    CMWorkTable* pWorkTable = GET_CHDIRECT(WORKTABLE, CMWorkTable);
    Q_ASSERT(pWorkTable);
    CMProduct* pProduct = GET_CHDIRECT(PRODUCT, CMProduct);
    Q_ASSERT(pProduct);
    double dLoadW = pWorkTable->m_Variants.get_VData(Var_MWorkTable_Load_W_ID)
        ->toDouble();
    Trans.CalcuParam(CMPointd(0.0, 0.0), -(pSide->m_dLevelAngle - dLoadW), CMPointd(1.0, 1.0));
    pSide->m_realCenter = Trans.AffineSrctoDest(pProduct->m_RealCenter);
    //LOG(INFO)<<"side angle:"<<pSide->m_dLevelAngle<<"side center:"<<pSide->m_realCenter.tX<<pSide->m_realCenter.tY;
    QString info = QString("TURN SIDE: %5 Angle:%1 CenterX:%2 CenterY:%3 %4").arg("side angle:").arg(pSide->m_dLevelAngle).arg(pSide->m_realCenter.tX).arg(pSide->m_realCenter.tY).arg(pSide->m_sName).arg(iSideID == SIDE_A ? "CH1" : "CH2");
    m_gSystem.LogInfo(info);
    AddProductData(info);
    //MqDebug() << "side angle:"<<pSide->m_dLevelAngle<<"side center:"<<pSide->m_realCenter.tX<<pSide->m_realCenter.tY;
    return 0;
}

void CProcess_Level::calcuLineCircle(int iSideID)
{
    CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
    Q_ASSERT(pSide);
    double dStepY = getCurSideStep(iSideID);
    CMPointd Top;
    Top.tX = m_AlignPtBase.Pt.tX;
    Top.tY = m_AlignPtBase.Pt.tY;
    pSide->m_dSolidBasePosX = m_AlignPtBase.Pt.tX;
    double dLeft, dRight, dBottom, dTop;
    //生成外接矩形
    getCircleBoundary(iSideID, dLeft, dRight, dBottom, dTop);
    MqDebug() << "Before Top: " << dTop;
    while (Top.tY < dTop) { Top.tY += dStepY; }
    MqDebug() << "Top out: " << dTop;
    while (Top.tY > dTop) { Top.tY -= dStepY; }

    //LOG(INFO) << "calcuLineCircle Side: " << (iSideID==SIDE_A?"CH1":"CH2") << "Center X:" << pSide->m_realCenter.tX << "Y:" << pSide->m_realCenter.tY 
        //<< " Base X " << m_AlignPtBase.Pt.tX <<"Base Y "<<m_AlignPtBase.Pt.tY <<"Angle:"<<pSide->m_dLevelAngle;

    //LOG(INFO) << "Left:" << dLeft << "Right:" << dRight << "Top:" << dTop << "Bottom:" << dBottom << "dStepY:" << dStepY;
    //子切割道，默认没有子切割道
    int isize = pSide->m_vSubIndex.size();
    QMutexLocker l(&pSide->m_CutLineVector.mutex);
    int ilineindex = 0;
    //生成切割线
    calculine(Top, dLeft, dRight, iSideID, FALSE, ilineindex, m_AlignPtBase.Pt.tY);
    if (isize) {
        for (int j = 0; j < isize; j++) {
            CMPointd top1 = Top;
            SUBINDEX subindex = pSide->m_vSubIndex[j];
            top1.tY -= subindex.dLocationOffset;
            calculine(top1, dLeft, dRight, iSideID, TRUE, j, m_AlignPtBase.Pt.tY);
            for (int i = 0; i < subindex.iCount; i++) {
                top1.tY -= subindex.dSubIndex;
                calculine(top1, dLeft, dRight, iSideID, TRUE, j, m_AlignPtBase.Pt.tY);
            }
        }
    }

    Top.tY -= dStepY;
    while (Top.tY > dBottom) {
        ilineindex++;
        calculine(Top, dLeft, dRight, iSideID, FALSE, ilineindex, m_AlignPtBase.Pt.tY);
        //默认没子切割道,下面的代码不执行
        if (isize) {
            for (int j = 0; j < isize; j++) {
                CMPointd top1 = Top;
                SUBINDEX subindex = pSide->m_vSubIndex[j];
                top1.tY -= subindex.dLocationOffset;
                calculine(top1, dLeft, dRight, iSideID, TRUE, j, m_AlignPtBase.Pt.tY);
                for (int i = 0; i < subindex.iCount; i++) {
                    top1.tY -= subindex.dSubIndex;
                    calculine(top1, dLeft, dRight, iSideID, TRUE, j, m_AlignPtBase.Pt.tY);
                }
            }
        }
        Top.tY -= dStepY;
    }
}

//生成切割道，并应用平台误差补偿
void CProcess_Level::calculine(CMPointd Bottom, double dLeft, double dRight,
                               int iSideID, BOOL bsub, int index, double baseY)
{
    CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
    Q_ASSERT(pSide);
    CMProduct* pProduct = GET_CHDIRECT(PRODUCT, CMProduct);
    Q_ASSERT(pProduct);
    double dStepX = getOtherSideStep(iSideID);
    CMPointd CenterPt = pSide->m_realCenter;
    double dRadius = pProduct->m_Variants.get_VData(Var_Product_Diameter_ID)
        ->toDouble() / 2;
    //pProduct->m_dRealDiameter/2;
    double dYOffset = fabs(Bottom.tY - CenterPt.tY);
    if (dYOffset > dRadius) { return; }
    double dXOffset = sqrtf(dRadius * dRadius - dYOffset * dYOffset);
    double XStart = CenterPt.tX - dXOffset;
    double XEnd = CenterPt.tX + dXOffset;
    if (XEnd > dRight) {
        XEnd = dRight;
    }
    if (XStart < dLeft) {
        XStart = dLeft;
    }

    //计算补偿值
    CWorktableOffset* pOffset = GET_CHDIRECT(WTOFFSET, CWorktableOffset);
    int iEnable = pOffset->m_Variants.get_VData(Var_WorkTable_Offset_EnableOffset)
        ->toBool();
    if (iEnable) {
        //偏移Y方向，平台位置补偿,默认禁用
        CWorktableOffsetDriver* pOffsetDriver = GET_CARD(WTOFFSET_DRIVER, CWorktableOffsetDriver);
        double dWorktableOffsetY = pOffsetDriver->GetOffset(WTOFFSET, Bottom.tY);
        double dBaseYOffset = pOffsetDriver->GetOffset(WTOFFSET, baseY);
        dWorktableOffsetY -= dBaseYOffset;

        //偏差应该小于7um,否则是错误的数据
        if (fabs(dWorktableOffsetY) > 0.007)
            MQT_THROW_CODESTR(ALARM_MAIN_LEVEING_USESAVEPT, QString("Worktable offset max range 0.007"));

        if (Bottom.tY<baseY - 0.01 || Bottom.tY>baseY + 0.01) {
            Bottom.tY -= dWorktableOffsetY;
            //LOG_INFO(fmt::format(" Line {} POS{}"index+1<<""<<Bottom.tY<<"Worktable Offset"<<dWorktableOffsetY<<"BaseOffset:"<<dBaseYOffset;
            QString info = QString("calculine Line:%1 Offset:%2 BaseYOffset:%3").arg(index + 1).arg(dWorktableOffsetY).arg(dBaseYOffset);
            AddProductData(info);
        } else {
            //如果是基准点，则不修正
            QString info = QString("calculine Line:%1 Ignore Offset BaseYOffset:%2").arg(index + 1).arg(dBaseYOffset);;
            AddProductData(info);
            //LOG(WARNING)<<__FUNCTION__<<" Ignore offset Line"<<index+1<<"Pos:"<<Bottom.tY<<"Worktable offset:"<<dWorktableOffsetY;
        }

        MqDebug() << Bottom.tY << "offset" << dWorktableOffsetY;
    }

    CUTLINE Line;
    Line.m_BasePt.tX = numDis((XStart + XEnd) / 2 - m_AlignPtBase.Pt.tX, dStepX) + m_AlignPtBase.Pt.tX;
    Line.m_BasePt.tY = Bottom.tY;
    Line.m_WPPair.ptNegative = CMPointd(XStart, Bottom.tY);
    Line.m_WPPair.ptPostive = CMPointd(XEnd, Bottom.tY);
    Line.m_bSUB = bsub;
    Line.m_index = index;
    pSide->m_CutLineVector.push_back(Line);
}

//重新更新调平点的坐标
void CProcess_Level::adjustPt(int iSideID, int ptstyle)
{
    double dMidStepOffset = m_Variants.get_VData(Var_Process_Level_MidStepOffset)
        ->toInt();
    double  dStepX = getOtherSideStep(iSideID);
    CMPointd refPt;
    switch (ptstyle) {
    case TYPE_PTROTATE:
        refPt = m_AlignPtRotate.Pt;
        //m_AlignPtBase.Pt=m_AlignPtRotate.Pt;
        //m_AlignPtBase.Pt.tX=numDis( m_AlignPtBase.Pt.tX-refPt.tX,dStepX)+refPt.tX;
        break;
    case TYPE_PTBASE:
        return;
    case TYPE_PT1:
        refPt = m_AlignPt1.Pt;
        break;
    case TYPE_PTMID:
        refPt = m_AlignPt1.Pt;
        return;
    case TYPE_PT2:
        refPt = m_AlignPt1.Pt;
        return;
    }

    CMPointd& LevelPt1 = m_AlignPt1.Pt;
    CMPointd& LevelPt2 = m_AlignPt2.Pt;
    CMPointd& LevelPtMid = m_AlignPtMid.Pt;
    CMPointd& LinePt = m_AlignPtBase.Pt;


    //在X的位置上调平，所以Y方向应该是一样的
    LinePt.tY = LevelPt1.tY = LevelPt2.tY = LevelPtMid.tY = refPt.tY;

    LinePt.tX = numDis(LinePt.tX - refPt.tX, dStepX) + refPt.tX;
    LevelPt1.tX = numDis(LevelPt1.tX - refPt.tX, dStepX) + refPt.tX;
    LevelPt2.tX = numDis(LevelPt2.tX - refPt.tX, dStepX) + refPt.tX;
    LevelPtMid.tX = LevelPt1.tX + dMidStepOffset * dStepX;
}

double CProcess_Level::calcuAngle(CMPointd Pt1, CMPointd Pt2)
{
    double dAngle = 0;
    CMPointd TempStart, TempEnd;
    if (fabs(Pt1.tX - Pt2.tX) < 0.1) {
        MQT_THROW_CODESTR(ALARM_MAIN_LEVEING_USESAVEPT, QObject::trUtf8("Calcu level Angle err.Some Point"));
    }
    //在右边的点为点1
    //在左边的点为点2
    //则Pt2右偏angle为负，Pt2在左边则angle为正
    TempStart = Pt1.tX > Pt2.tX ? Pt1 : Pt2;
    TempEnd = Pt1.tX < Pt2.tX ? Pt1 : Pt2;
    double dX = TempStart.tX - TempEnd.tX;
    double dY = TempStart.tY - TempEnd.tY;

    double temp = sqrtf(dY * dY + dX * dX);
    dAngle = DEG(asin(dY / temp));

    return dAngle;
}

//计算目标点与点1的旋转角度
double CProcess_Level::calcuAngle(int ptstyle)
{
    double dAngle = 0;
    switch (ptstyle) {
    case TYPE_PTROTATE:  dAngle = m_AlignPtRotate.dAngle; break;
    case TYPE_PTMID:  dAngle = calcuAngle(m_AlignPt1.Pt, m_AlignPtMid.Pt); break;
    case TYPE_PT2: dAngle = calcuAngle(m_AlignPt1.Pt, m_AlignPt2.Pt); break;
    default: Q_ASSERT(0);
    }
    return dAngle;
}

void CProcess_Level::Turn_Angle(int iSideID, int ptstyle)
{
    double dangle = calcuAngle(ptstyle);
    turn(iSideID, dangle);
    adjustPt(iSideID, ptstyle);
}

//手动设置点
void CProcess_Level::SetPtManual(int iSideID, int iCamID,
                                 int ptstyle, int iLineID,
                                 bool bManualSet/* = true*/)
{
    CMWorkTableDriver* pWorktableDriver = GET_CARD(WORKTABLE_DRIVER,
                                                   CMWorkTableDriver);
    Q_ASSERT(pWorktableDriver);

    CImageContainer* pContainer = 0;
    int index = iSideID == SIDE_A ? 0 : 1;
    QString str;
    CMPointd wt;
    pWorktableDriver->CurAxisToWt(iCamID, wt, true);
    switch (ptstyle) {
    case TYPE_PT1:
        m_AlignPt1.Pt = wt;
        m_AlignPt1.bHasSet = true;
        pContainer = &(m_imageContainerAlign[index][0]);
        str = QObject::trUtf8("Manual Set Pt1");
        AddProductData(QString("Manual Set Pt1 x:%1 y:%2 at:%3").arg(wt.tX).arg(wt.tY).arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz")));
        break;
    case TYPE_PTMID:
        m_AlignPtMid.Pt = wt;
        m_AlignPtMid.bHasSet = true;
        //更新点
        turn(iSideID, calcuAngle(TYPE_PTMID));
        //调整后，重新移动到中点
        MovePt(iSideID, iCamID, TYPE_PTMID, false, 0, 0);
        pContainer = &(m_imageContainerAlign[index][1]);
        str = QObject::trUtf8("Manual Set PtMid");
        AddProductData(QString("Manual Set PtMid x:%1 y:%2 at:%3").arg(wt.tX).arg(wt.tY).arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz")));
        break;
    case TYPE_PT2:
        m_AlignPt2.Pt = wt;
        m_AlignPt2.bHasSet = true;
        turn(iSideID, calcuAngle(TYPE_PT2));
        //调整后，重新移动到第二点
        MovePt(iSideID, iCamID, TYPE_PT2, false, 0, 0);
        pContainer = &(m_imageContainerAlign[index][1]);
        str = QObject::trUtf8("Manual Set Pt2");
        AddProductData(QString("Manual Set Pt2 x:%1 y:%2 at:%3").arg(wt.tX).arg(wt.tY).arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz")));
        break;
    case TYPE_PTBASE:
    {
        double dOffset = wt.tY - m_AlignPtBase.Pt.tY;
        if (fabs(dOffset) > getCurSideStep(iSideID) / 2) {
            MQT_THROW_CODESTR(ALARM_MAIN_ALIGN_MANUALOFFSET_GreaterHalfIndex, QObject::trUtf8("Position set can not over half index"));
        }
        m_AlignPtBase.Pt = wt;
        m_AlignPtBase.bHasSet = true;
        calcuSideVector(iSideID, dOffset, iLineID, bManualSet);

        CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
        Q_ASSERT(pSide);
        pContainer = &(m_imageContainerBase[index]);
        str = QObject::trUtf8("Manual Set PtBase");

        AddProductData(QString("Manual Set PtBase  Offset:%1 Line:%2 at:%3").arg(dOffset).arg(iLineID + 1).arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz")));
        pSide->WriteRecoveredData();
        pSide->RecordLineData();
        break;
    }
    case TYPE_PTROTATE:
    {
        //旋转点手动时只进行偏移
        CMPointd ptOffset = wt - m_AlignPtRotate.Pt;
        m_AlignPt1.Pt += ptOffset;
        m_AlignPtMid.Pt += ptOffset;
        m_AlignPt2.Pt += ptOffset;
        m_AlignPtBase.Pt += ptOffset;
        m_AlignPtRotate.Pt += ptOffset;
        m_AlignPtRotate.bHasSet = true;
        pContainer = &(m_imageContainerBase[index]);
        str = QObject::trUtf8("Manual Set PtRotate");
        CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
        Q_ASSERT(pSide);
        CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
        pSide->m_dLevelAngle = pDriver->get_posdata(MOTORW, true);
        AddProductData(QString("Manual Set PtRotate Angle:%1 at:%2").arg(pSide->m_dLevelAngle).arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz")));
        break;
    }
    }
    CaptureImage(iCamID, pContainer, str, false);

    adjustPt(iSideID, ptstyle);
}

int CProcess_Level::Test_Model(int iSideID, int iCamID)
{
    int iCount = m_Variants.get_VData(Var_Process_Level_ModelTestCount)
        ->toInt();
    CMWorkTableDriver* pWorktableDriver = GET_CARD(WORKTABLE_DRIVER,
                                                   CMWorkTableDriver);
    Q_ASSERT(pWorktableDriver);
    CMPointd wt, wt1;
    pWorktableDriver->CurAxisToWt(iCamID, wt, true);
    wt1 = wt;
    double dStepCur = getCurSideStep(iSideID);
    double dStepOther = getOtherSideStep(iSideID);
    CMPointd WTResult;
    double dAngle;
    bool bresult = search_pt(iSideID, WTResult, dAngle,
                             iCamID, true, 0, false);
    if (!bresult) {
        MQT_THROW_CODESTR(ALARM_MAIN_LOGIC_PARAM, QObject::trUtf8("Test Model failed"));
    }
    wt1 = WTResult;
    wt = WTResult;
    for (int i = 0; i < iCount; i++) {
        wt1.tY += dStepCur;
        posMove(iSideID, wt1, iCamID);
        bool bresult = search_pt(iSideID, WTResult, dAngle,
                                 iCamID, true, 0, false);
        if (!bresult) {
            MQT_THROW_CODESTR(ALARM_MAIN_LOGIC_PARAM, QObject::trUtf8("Test Model failed"));
        }
        wt1 = WTResult;
    }
    wt1 = wt;
    for (int i = 0; i < iCount; i++) {
        wt1.tY -= dStepCur;
        posMove(iSideID, wt1, iCamID);
        bool bresult = search_pt(iSideID, WTResult, dAngle,
                                 iCamID, true, 0, false);
        if (!bresult) {
            MQT_THROW_CODESTR(ALARM_MAIN_LOGIC_PARAM, QObject::trUtf8("Test Model failed"));
        }
        wt1 = WTResult;
    }
    wt1 = wt;
    for (int i = 0; i < iCount; i++) {
        wt1.tX -= dStepOther;
        posMove(iSideID, wt1, iCamID);
        bool bresult = search_pt(iSideID, WTResult, dAngle,
                                 iCamID, true, 0, false);
        if (!bresult) {
            MQT_THROW_CODESTR(ALARM_MAIN_LOGIC_PARAM, QObject::trUtf8("Test Model failed"));
        }
        wt1 = WTResult;
    }
    wt1 = wt;
    for (int i = 0; i < iCount; i++) {
        wt1.tX += dStepOther;
        posMove(iSideID, wt1, iCamID);
        bool bresult = search_pt(iSideID, WTResult, dAngle,
                                 iCamID, true, 0, false);
        if (!bresult) {
            MQT_THROW_CODESTR(ALARM_MAIN_LOGIC_PARAM, QObject::trUtf8("Test Model failed"));
        }
        wt1 = WTResult;
    }
    wt1 = wt;
    posMove(iSideID, wt1, iCamID);
    m_ModelTestPos = wt1;
    m_gSystem.m_SysMsg.SetMsg(QObject::trUtf8("Test Model Success"));
    return MSG_FINISHED;
}

int CProcess_Level::FindModelOnce(int iSideID, int iCamID)
{
    int iCount = m_Variants.get_VData(Var_Process_Level_ModelTestCount)
        ->toInt();
    CMWorkTableDriver* pWorktableDriver = GET_CARD(WORKTABLE_DRIVER,
                                                   CMWorkTableDriver);
    Q_ASSERT(pWorktableDriver);
    CMPointd wt, wt1;
    pWorktableDriver->CurAxisToWt(iCamID, wt, true);
    wt1 = wt;
    double dStepCur = getCurSideStep(iSideID);
    double dStepOther = getOtherSideStep(iSideID);
    CMPointd WTResult;
    double dAngle;
    bool bresult = search_pt(iSideID, WTResult, dAngle,
                             iCamID, true, 0, false);
    if (!bresult) {
        MQT_THROW_CODESTR(ALARM_MAIN_LOGIC_PARAM, QObject::trUtf8("Test Model failed"));
    }
    wt1 = WTResult;
    posMove(iSideID, wt1, iCamID);

    bresult = search_pt(iSideID, WTResult, dAngle,
                        iCamID, true, 0, false);
    if (!bresult) {
        MQT_THROW_CODESTR(ALARM_MAIN_LOGIC_PARAM, QObject::trUtf8("Test Model failed"));
    }
    m_ModelTestPos = wt1;
    m_gSystem.m_SysMsg.SetMsg(QObject::trUtf8("Test Model Success"));
    return MSG_FINISHED;
}

void CProcess_Level::posMove(int iSideID, const CMPointd& WPDP, int iCamID)
{
    UNUSED(iSideID);
    m_gSystem.changeChannel(iCamID, false);
    CMWorkTableDriver* pWorkTableDriver = GET_CARD(WORKTABLE_DRIVER, CMWorkTableDriver);
    Q_ASSERT(pWorkTableDriver);

    CMPointd Axis;
    pWorkTableDriver->WtToAxis(iCamID, WPDP, Axis);
    m_gSystem.absmove_xy(Axis.tX, Axis.tY);
}

void CProcess_Level::SetLevelLight(int iSideID, int iCamID)
{
    CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
    Q_ASSERT(pSide);
    MODEL_INFO info = pSide->get_Model_info(iCamID);
    int idirect = info.iDirectLight;
    int iside = info.iSideLight;
    m_gSystem.SetCamLight(iCamID, idirect, true, false);
    m_gSystem.SetCamLight(iCamID, iside, false, false);
}

void CProcess_Level::SetCamParam(int iSideID, int iCamID)
{
    CProductSide* pSide = GET_CHDIRECT(iSideID, CProductSide);
    Q_ASSERT(pSide);
    MODEL_INFO info = pSide->get_Model_info(iCamID);
    m_gSystem.SetCamParam(iCamID, info.iExposureUs, info.iGain);
}

double CProcess_Level::numDis(double dDis, double dStep)
{
    if (DEQUAL0(dDis)) {
        return 0;
    }
    if (DEQUAL0(dStep)) {
        MQT_THROW_CODESTR(ALARM_MAIN_LOGIC_PARAM, QObject::trUtf8("Level calcu dis err."
                                                                  "step equal 0"));
    }
    if (dDis > 0) {
        return int(dDis / dStep + 0.1) * dStep;
    }
    return int(dDis / dStep - 0.1) * dStep;
}
