#include "logic.h"
#include "cprocess_coat.h"
#include "cprocess_trans.h"
#include "CoatVolumeSensor.h"
#include "cmotordriver_acs.h"
#define ACTION_LENGTH 13
const char* CoatName[ACTION_LENGTH] =
{
    "INIT",
    "COAT",
    "COAT(Spiral)",
    "WATER(SOAK)",
    "WASH",
    "SPIN",
    "AIR",
    "FCLN",     //洗铁环
    "FCLN(DRY)",
    "WAIT",
    "OpenCutain",
    "CloseCutain",
};

CProcess_Coat::CProcess_Coat(const QString& name, int32_t id)
    :CProcess_CoatClean(name, id)
{

    m_Variants.add_Variant(Var_Coat_Nozzel_ShutSleep_ID,
                           QObject::trUtf8("SleepAfterShutNozzel"), int(0), false,
                           Variant::TOXML);

    m_Variants.add_Variant(Var_Coat_Clean_Arm_WaitPos_ID,
                           QObject::trUtf8("CleanWaitPos"), double(0.1), false,
                           Variant::TOXML);
    m_Variants.add_Variant(Var_Coat_Clean_Arm_EagePos_ID,
                           QObject::trUtf8("CleanEagePos"), double(1), false,
                           Variant::TOXML);
    m_Variants.add_Variant(Var_Coat_Clean_Arm_CenterPos_ID,
                           QObject::trUtf8("CleanCenterPos"), double(45), false,
                           Variant::TOXML);
    m_Variants.add_Variant(Var_Coat_Clean_Arm_RingPos_ID,
                           QObject::trUtf8("CleanRingPos"), double(2), false,
                           Variant::TOXML);
    m_Variants.add_Variant(Var_Coat_Clean_Arm_PerparePos_ID,
                           QObject::trUtf8("CleanPerparePos"), double(0.1), false,
                           Variant::TOXML);

    m_Variants.add_Variant(Var_Coat_Arm_WaitPos_ID,
                           QObject::trUtf8("CoatWaitPos"), double(0.1), false,
                           Variant::TOXML);
    m_Variants.add_Variant(Var_Coat_Arm_EagePos_ID,
                           QObject::trUtf8("CoatEagePos"), double(1), false,
                           Variant::TOXML);
    m_Variants.add_Variant(Var_Coat_Arm_CenterPos_ID,
                           QObject::trUtf8("CoatCenterPos"), double(45), false,
                           Variant::TOXML);
    m_Variants.add_Variant(Var_Coat_Arm_PerparePos_ID,
                           QObject::trUtf8("CoatPerparePos"), double(0.1), false,
                           Variant::TOXML);

    m_Variants.add_Variant(Var_Coat_Spay_ZeroOffset_ID,
                           QObject::trUtf8("CoatGlueZeroPos"), double(0), false,
                           Variant::TOXML);

    m_Variants.add_Variant(Var_Coat_Arm_Speed_ID,
                           QObject::trUtf8("CleanNozzelSpeed"), double(0), false,
                           Variant::TOXML);

    m_Variants.add_Variant(Var_Coat_Spindle_InitPos_ID,
                           QObject::trUtf8("SpindleAfterCoatPos"), double(0), false,
                           Variant::TOXML);

    m_Variants.add_Variant(Var_Coat_Bubble_Thres_ID,
                           QObject::trUtf8("BubbleWarnCount"), int(0), false,
                           Variant::TOXML);

    m_Variants.add_Variant(Var_Coat_Protect_OpenPos_ID,
                           QObject::trUtf8("Protect_OpenPos"), double(0), false,
                           Variant::TOXML);
    m_Variants.add_Variant(Var_Coat_Protect_ClosePos_ID,
                           QObject::trUtf8("Protect_ClosePos"), double(0), false,
                           Variant::TOXML);

    m_Variants.add_Variant(Var_Coat_Spay_Pre_Num_ID,
                           QObject::trUtf8("PrepareSpay_ml"), double(0), false,
                           Variant::TOXML);
    m_Variants.add_Variant(Var_Coat_Spay_Pre_WaitTime,
                           QObject::trUtf8("PrepareSpay_WaitTime"), int(0), false,
                           Variant::TOXML);


    //涂敷的Recipe参数,喷胶量
    m_Variants.add_Variant(Var_Coat_Spay_Num_ID,
                           QObject::trUtf8("CoatGlue_ml"), double(0), false,
                           Variant::TOBOTH);
    m_Variants.add_Variant(Var_Coat_Spay_Spd_ID,
                           QObject::trUtf8("CoatGlueSpeed"), double(0), false,
                           Variant::TOBOTH);
    m_Variants.add_Variant(Var_Coat_M_CenterSpeed_ID,
                           QObject::trUtf8("CoatMCenterSpeed"), double(0), false,
                           Variant::TOBOTH);
    m_Variants.add_Variant(Var_Coat_M_EageSpeed_ID,
                           QObject::trUtf8("CoatMEageSpeed"), double(0), false,
                           Variant::TOBOTH);
    m_Variants.add_Variant(Var_Coat_M_CenterWaitTime_ID,
                           QObject::trUtf8("CoatMCenterWaitTime"), double(0), false,
                           Variant::TOBOTH);

    //最大抽胶量
    m_Variants.add_Variant(Var_Coat_Bump_Max_ID,
                           QObject::trUtf8("MaxBumpVolume"), double(50), false,
                           Variant::TOXML);

    m_Variants.add_Variant(Var_Coat_ReplaceFluidVolume,
                           QObject::trUtf8("ReplaceCoatingFluidVolume"), double(160), false,
                           Variant::TOXML);
    //清洁保护块的位置
    m_Variants.add_Variant(Var_Coat_Clean_ProtectionBlockPos,
                           QObject::trUtf8("WashProtectionBlockPos"), double(1.5), false,
                           Variant::TOXML);

    m_dVolumeBefore = 0;
    m_dVolumeAfter = 0;
    m_dVolumeActual = 0;

    m_dTotalBump = 0;
    m_dSingleBump = 0;
    m_BumpTargetVolume = 0;
}

QString CProcess_Coat::GetActionName(int iActionID)
{
    if (iActionID < 0 || iActionID >= ACTION_LENGTH) { return QObject::trUtf8("N/A"); }
    return QObject::trUtf8(CoatName[iActionID]);
}

void CProcess_Coat::do_msg(int iID, QVariantList& vlInput, V_MsgOutput* pvlOutput)
{
    UNUSED(pvlOutput);
    int32_t isize = vlInput.size();
    switch (iID) {
    case BUMP_TEST:
        if (isize >= 1) {
            Bump_Test(vlInput[0].toInt());
        }
        break;

    case SPARY_TEST:
        if (isize >= 1) {
            Spary_Test(vlInput[0].toInt());
        }
        break;
    case REPLACE_COATFLUID:
        if (isize >= 1) {
            ReplaceCoatFluid(vlInput[0].toInt());
        }
        break;
    case SINGLE_ACTION_BEFORE:
        if (isize >= 5) {
            double bumpNum = 0; //抽胶量
            double preSpyNum = 0; //预喷胶量
            bumpNum = vlInput[12].toDouble();
            preSpyNum = vlInput[13].toDouble();
            double coatPreSpyNum = vlInput[10].toDouble();
            double coatPreSpyWaitTime = vlInput[11].toInt();
            single_action_before(vlInput[0].toInt(), vlInput[1].toInt(),
                                 vlInput[2].toDouble(), vlInput[3].toInt(), vlInput[4].toInt(), vlInput[5].toDouble(),
                                 vlInput[6].toDouble(), vlInput[7].toDouble(), vlInput[8].toDouble(), vlInput[9].toDouble(),
                                 coatPreSpyNum, coatPreSpyWaitTime,
                                 bumpNum, preSpyNum);
        }
        break;
    default:
        CProcess_CoatClean::do_msg(iID, vlInput, pvlOutput);
        break;
    }
}

void CProcess_Coat::all_action_before(int iActionType, int iStep)
{
    if (iStep == 0) {
        
        AssertAcuireRes(SM_COAT, kResCoat, "Start Coat");

        AddProductData(QString("Coat start at :") + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz"));
        if (iActionType == NORMAL_ACTION || iActionType == CLEANCOATFLUID_ACTION) {
            //清洗平台或涂覆操作
            CMotorDriver* pMotorDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
            Q_ASSERT(pMotorDriver);
            pMotorDriver->setio(IO_OUTPUT, OUT_COAT_VACUUM_BREAK, false);
            m_gSystem.VacuumSet(true, OUT_COAT_VACUUM, IN_COAT_VACUUM);
        } else if (iActionType == CLEANTABLE_ACTION) {
            //用空气清洁涂覆台
            bool bExist = m_gSystem.CellExist(OUT_COAT_VACUUM, IN_COAT_VACUUM);
            if (bExist) {
                MQT_THROW_CODESTR(ALARM_COAT_SAFE_PROTECT, QObject::trUtf8("take off wafer before clean coat table."));
            }

            CMotorDriver* pMotorDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
            Q_ASSERT(pMotorDriver);
            pMotorDriver->setio(IO_OUTPUT, OUT_COAT_VACUUM_BREAK, true);
        } else {
            Q_ASSERT(0 && "not support coat type in all_action_before");
        }
        SpindleUp(false);

        m_gSystem.EvtLog(QObject::tr("COAT"), QObject::tr("WaferID:%1,COAT Starting").arg(m_gSystem.GetCoatWaferId().c_str()));
    }
    if (iStep == 1) {
        CCoatVolumeSensor* pVolumeSensor = GET_CARD(COATVOLUME_SENSOR, CCoatVolumeSensor);
        if (true == pVolumeSensor->ReadGrams(2)) {
            m_dVolumeBefore = pVolumeSensor->m_dGrams;
            m_gSystem.LogInfo(QString("Coat before record glue volume:") + QString::number(pVolumeSensor->m_dGrams) + " ml");
            AddProductData(QString("Coat before record glue volume:") + QString::number(pVolumeSensor->m_dGrams) + " ml");
        } else {
            m_gSystem.LogError(QString("Coat before get weight data failed.Before ") + QString::number(m_dVolumeBefore) + " ml");
            AddProductData(QString("Coat before get weight data failed.Before ") + QString::number(m_dVolumeBefore) + " ml");
        }

        double dAlarmVolume = pVolumeSensor->m_Variants.get_VData(Var_CoatVolume_AlarmVolume)->toDouble();
        //胶量不足需要换胶
        if (m_dVolumeBefore < dAlarmVolume) {
            MQT_THROW_CODESTR(ALARM_COAT_FLUID_CoatingLiquidInsufficient, QObject::tr("Coat liquid volume %1 ml less than %2 ml").arg(m_dVolumeBefore, 0, 'f', 1).arg(dAlarmVolume, 0, 'f', 1));
        }

        m_dTotalBump = 0;
        m_dSingleBump = 0;
        m_dVolumeActual = 0;
    } else if (iStep == 2) {
        //洗胶池水打开
        CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
        Q_ASSERT(pDriver);
        pDriver->setio(IO_OUTPUT, OUT_COAT_CLEANING_GLUE_WATER, TRUE);
        //TODO:静电消除器打开
        //pDriver->setio(IO_OUTPUT, OUT_ELECT_Coat, true);

        CloseProtect(true);   //防水帘
    }
    CProcess_CoatClean::all_action_before(iActionType, iStep);
}

//iStep = 0 ~ 200
void CProcess_Coat::single_action_before(int iActionType, int32_t iType, double dTime, int iRPM, int iStep,
                                         double dSpayNum, double dSpaySpd, double dCenterSpeed, double dEageSpeed, double dCenterWaitTime,
                                         double dCoatPreSpyNum, int iCoatPreSpyWaitTime,
                                         double dBumpNum, double dPreSpyNum)
{
    //清洗->吹干>涂敷>
    UNUSED(dTime);
    UNUSED(iActionType);
    if (iStep == 0) {
        AddProductData(QString("Coat Step:%1\t%2").arg(GetActionName(iType)).arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz")));
        if (iType == CLOSE_CURTAIN) {
            AddProductData(QString("\tClose Curtain"));
        } else if (iType == OPEN_CURTAIN) {
            AddProductData(QString("\tOpen Curtain"));
        } else if (iType == COAT || iType == COAT_M) {
            AddProductData(QString("\tRunTime:%2s RPM:%3 SPay Volume:%4 SpaySpeed:%5 CenterSpeed:%6 EageSpeed:%7 CenterWaitTime:%8")
                           .arg(GetActionName(iType)).arg(dTime).arg(iRPM).arg(dSpayNum).arg(dSpaySpd).arg(dCenterSpeed).arg(dEageSpeed).arg(dCenterWaitTime));
        } else {
            AddProductData(QString("\tRunTime:%1s RPM:%2 CoatPreSpay:%3ml PreSpayWaitTime:%4ms").arg(dTime).arg(iRPM).arg(dCoatPreSpyNum).arg(iCoatPreSpyWaitTime));
            AddProductData(QString("\tBumpVolume:%4ml PreSpay:%5ml").arg(dBumpNum).arg(dPreSpyNum));
        }
    }

    //开关防水帘
    if (iType == CLOSE_CURTAIN
        || iType == OPEN_CURTAIN) {
        if (iStep == 1) {
            if (iType == CLOSE_CURTAIN) {
                CloseProtect(true, false);   //防水帘
            } else {
                CloseProtect(false, false);   //防水帘
            }
            return CProcess_CoatClean::single_action_before(iActionType, iType, dTime, iRPM, iStep);
        } else {
            return CProcess_CoatClean::single_action_before(iActionType, iType, dTime, iRPM, iStep);
        }
    }

    if (iStep == 1)    nozzeloperator_coat(INIT_COAT);
    if (iStep == 5)    SpindleUp(false);   //主轴降下
    if (iStep == 10) CoatArmUp(true);      //要升起来才能摆臂
    //if(iStep==15) CloseProtect(true);   //防水帘
    if (iStep == 20) CleanArmUp(true);     //清洗臂可以直接摆
    if (iStep == 25) MoveCoatWaitPos();    //移动到准备位置，清洗轴不能升降，直接摆动，但是清洗轴跟涂胶轴不能冲突
    if (iStep == 30) MoveCleanWaitPos();   //清洗臂到准备位置
    if (iStep == 35)
    {
        //旋转前需要检查真空是否OK
        if (iActionType == NORMAL_ACTION)
        {
            CheckVaccum();
        }
        SpindleSpin(iRPM);    //主轴旋转，负方向
    }
    //TODO:开始执行吸胶操作
    if (iStep == 36) {
        m_dSingleBump = 0;
        if (iType != COAT && iType != COAT_M
            && !DEQUAL0(dBumpNum) && dBumpNum > 0) {
            BumpNoWait(dBumpNum, m_BumpTargetVolume, m_dSingleBump);
        }
        //
        if (iType != COAT && iType != COAT_M
            && DEQUAL0(dBumpNum) && !DEQUAL0(dPreSpyNum)) {
            SprayPrepareNoWait(dPreSpyNum);
        }
    }
    if (iStep == 37) {
        //计算实际胶量
        m_dTotalBump += m_dSingleBump;
        m_dSingleBump = 0;
    }
    switch (iType) {
    case COAT:
    {
        //旋转涂覆
        if (iStep == 39) CoatArmUp(true);		//升起摆臂
        if (iStep == 40) MoveCoatWaitPos(); //移动到涂敷准备位置,之前已经升起来了
        if (iStep == 45) Bump(dSpayNum + dCoatPreSpyNum, m_dSingleBump, false);  //吸,不排空
        if (iStep == 46) {
            //计算实际吸胶量
            m_dTotalBump += m_dSingleBump;
            m_dSingleBump = 0;
        }
        if (iStep == 50) Spray_prepare(dCoatPreSpyNum, 0, iCoatPreSpyWaitTime);     //预喷胶,预喷胶完成后等待一时间

        if (iStep == 51) {
            MSleep(m_gSystem.m_iCoatPreSpayWaitTimeMs);
            MoveCoatPreparePos();	//移动到准备位置
        }
        if (iStep == 55) MoveCoatCenterPos(); //摆臂到中心
        if (iStep == 59)
        {
            Spray(dSpayNum, true, dSpaySpd);          //唝胶并等待结束
            SetWaferHasWafer(true);
        }
        if (iStep == 60) CoatCenterWait(dCenterWaitTime);
        if (iStep == 61) MoveCoatPreparePos();	//移动到准备位置
        if (iStep == 65) MoveCoatWaitPos();   //涂胶臂移动到等待位置
        break;
    }
    case COAT_M:
    {
        //固定涂覆
        if (iStep == 39) CoatArmUp(true);		//升起摆臂
        if (iStep == 40) MoveCoatWaitPos();
        if (iStep == 45) Bump(dSpayNum + dCoatPreSpyNum, m_dSingleBump, false);
        if (iStep == 46) {
            //计算实际吸胶量
            m_dTotalBump += m_dSingleBump;
            m_dSingleBump = 0;
        }
        if (iStep == 50) Spray_prepare(dCoatPreSpyNum, 0, iCoatPreSpyWaitTime); //预喷胶
#ifdef __DEFINE_UP_COAT__
#else
        if (iStep == 51) MoveCoatPreparePos();	//移动到准备位置

#endif
        if (iStep == 55) MoveCoatCenterPos(true);
        if (iStep == 60)
        {
            Spray(dSpayNum, false, dSpaySpd);
            SetWaferHasWafer(true);
        }
        if (iStep == 61) CoatCenterWait(dCenterWaitTime);  //中心胶后等待一会
        if (iStep == 70) MoveCoatMultiSpd(false, dCenterSpeed, dEageSpeed);  //多点运动
        if (iStep == 75) { Wait_Spray_Finish(); }   //喷出结束，HACK：这里没有做联动，没办法保证喷胶完刚好走完
        if (iStep == 76) {
            //等待涂敷手臂运动结束 
            Wait_CoatArm_finish();
        }
        //主轴在AfterAllAction时才结束Stop，吐完了主轴做什么操作？？（后续还要旋转主轴，所以没有停？？）    
        //移动到等待位置
        if (iStep == 77) {
            MSleep(m_gSystem.m_iCoatPreSpayWaitTimeMs);
            MoveCoatPreparePos();	//移动到准备位置
        }
        if (iStep == 80) MoveCoatWaitPos();
        break;
    }
    case WATER_SOAK:
    {
        if (iStep == 40) MoveCleanEagePos();
        if (iStep == 45) nozzeloperator_coat(WATER_SOAK);	//浸润
        if (iStep == 50) MoveCleanCyclic();
        break;
    }
    case WASH:  //清洗操作
    {
        if (iStep == 40) MoveCleanEagePos();
        if (iStep == 45) nozzeloperator_coat(WASH);
        if (iStep == 50) MoveCleanCyclic();
        break;
    }
    case DRY://SPIN
    {
        //主轴旋转即可
        if (iStep == 40)
        {
            nozzeloperator_coat(DRY);
        }
        break;
    }
    case DRY_M:
    {
        //TODO:主气体装在哪里不清楚
        if (iStep == 40) MoveCleanEagePos();
        if (iStep == 45) nozzeloperator_coat(DRY_M);
        if (iStep == 46) MoveCleanEagePos();
        if (iStep == 50) MoveCleanCyclic();
        /*if(iStep==40) MoveCleanEagePos( );          //吹气干燥
        if(iStep==45) nozzeloperator_coat(DRY_M);
        if(iStep==50) MoveCleanCenterPos( );
        if(iStep==55) MoveCleanEagePos( );
        if(iStep==60) MoveCleanCenterPos( );
        if(iStep==65) MoveCleanEagePos( );
        if(iStep==70) nozzeloperator_coat(INIT_COAT);
        if(iStep==75) MoveCleanWaitPos( );*/

        break;
    }
    case FCLN:
    {
        if (iStep == 40) MoveCleanPreparePos();           //洗铁环
        if (iStep == 45) MoveCleanRingPos();  //移动到清洗铁环位置
        if (iStep == 50) nozzeloperator_coat(FCLN);
        break;
    }
    case FCLN_DRY:
    {
        if (iStep == 40) MoveCleanPreparePos();           //洗铁环
        if (iStep == 45) nozzeloperator_coat(FCLN_DRY);
        if (iStep == 50) MoveCleanRingPos();  //移动到清洗铁环位置
        break;
    }
    case WAIT:
        //仅主轴旋转
        if (iStep == 40)
        {
            nozzeloperator_coat(INIT_COAT);
            SetWaferHasWafer(false);
        }
        break;
    }
    if (iStep == 85) {
        //处理抽胶和预喷胶
        if (iType != COAT && iType != COAT_M
            && !DEQUAL0(dBumpNum) && dBumpNum > 0) {
            WaitBumpFinished(dBumpNum, m_BumpTargetVolume);
        }

        if (iType != COAT && iType != COAT_M
            && DEQUAL0(dBumpNum) && !DEQUAL0(dPreSpyNum)) {
            WaitSparyPrepareFinished(dPreSpyNum);

            CCoatVolumeSensor* pVolumeSensor = GET_CARD(COATVOLUME_SENSOR, CCoatVolumeSensor);
            if (true == pVolumeSensor->ReadGrams(2)) {
                m_dVolumeAfter = pVolumeSensor->m_dGrams;

                m_dVolumeActual = m_dVolumeBefore - m_dVolumeAfter;
            } else {
                LOG_ERR(fmt::format("Single get weight data failed.Before {}  ml After: {} ml", m_dVolumeBefore, m_dVolumeAfter));;
            }
        }

        if (iType == COAT || iType == COAT_M) {
            CCoatVolumeSensor* pVolumeSensor = GET_CARD(COATVOLUME_SENSOR, CCoatVolumeSensor);
            if (true == pVolumeSensor->ReadGrams(2)) {
                m_dVolumeAfter = pVolumeSensor->m_dGrams;

                m_dVolumeActual = m_dVolumeBefore - m_dVolumeAfter;
            } else {
                LOG_ERR(fmt::format("Single get weight data failed.Before {} ml After: {} ml", m_dVolumeBefore, m_dVolumeAfter));
            }
        }
    }
    CProcess_CoatClean::single_action_before(iActionType, iType, dTime, iRPM, iStep);
}

bool CProcess_Coat::single_action_wait(int iActionType, int32_t iType, double dTime, int iRPM, int iStep)
{
    UNUSED(iStep);
    UNUSED(iRPM);
    if (iActionType == NORMAL_ACTION) {
        CheckVaccum();
    } else if (iActionType == CLEANTABLE_ACTION) {} else if (iActionType == CLEANCOATFLUID_ACTION) {
        CheckVaccum();
    } else { Q_ASSERT(0); }
    switch (iType) {
    case COAT:
    {
        break;
    }
    case COAT_M:
    {
        break;
    }
    case WASH:
    case WATER_SOAK:
    {
        CheckWater();
        break;
    }
    case DRY:
    {
        break;
    }
    case DRY_M:
    {
        break;
    }
    case FCLN:
    {
        CheckWater();
        break;
    }
    case WAIT:
    {
        break;
    }
    }
    return CProcess_CoatClean::single_action_wait(
        iActionType, iType, dTime, iRPM, iStep);
}

void CProcess_Coat::single_action_after(int iActionType, int32_t iType, double dTime, int iRPM, int iStep)
{

    if (iStep == 0) {
        AddProductData(QString("\tCoat Step End:%1\t%2").arg(GetActionName(iType)).arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz")));
    }

    CProcess_CoatClean::single_action_after(
        iActionType, iType, dTime, iRPM, iStep);
    if (iStep == 5) nozzeloperator_coat(INIT_COAT);
#ifdef __DEFINE_UP_COAT__
#else
    if (iStep == 6) CoatArmUp(true);
#endif
    if (iStep == 15)MoveCoatWaitPos();
    if (iStep == 20)MoveCleanWaitPos();

}

void CProcess_Coat::all_action_after(int iActionType, int iStep)
{
    if (iStep == 1) {
        if (iActionType == NORMAL_ACTION) {
        } else if (iActionType == CLEANCOATFLUID_ACTION) {

        } else if (iActionType == CLEANTABLE_ACTION) {
            CMotorDriver* pMotorDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
            Q_ASSERT(pMotorDriver);
            pMotorDriver->setio(IO_OUTPUT, OUT_COAT_VACUUM_BREAK, false);
        } else { Q_ASSERT(0); }

        //洗胶池水关闭
        CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
        Q_ASSERT(pDriver);
        pDriver->setio(IO_OUTPUT, OUT_COAT_CLEANING_GLUE_WATER, false);

        SpindleStop();
        SetdefaultSpeed();
        //CloseProtect(false);   //防水帘
    }

    CMotorDriver* pMotorDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pMotorDriver);

    //if (pMotorDriver->get_iodata(IO_INPUT, IN_COAT_SPINDLE_DOWN_BACK, true)) {
    if (iStep == 10)nozzeloperator_coat(INIT_COAT);
    if (iStep == 15)CoatArmUp(true);
    if (iStep == 25)MoveCoatWaitPos();
    if (iStep == 30)MoveCleanWaitPos();
    //}
    if (iStep == 40) {
        CloseProtect(true, false);             //不打开防水帘，在传片时才打开
        MoveSpindleInitPos(true, false);
    }
    if (iStep == 45)   CoatArmUp(false);
    if (iStep == 46) {
        CloseProtect(true, true);
        Wait_Spindle_finish();
    }
    if (iStep == 50)   SpindleUp(true);

    if (iStep == 60) {
        CCoatVolumeSensor* pVolumeSensor = GET_CARD(COATVOLUME_SENSOR, CCoatVolumeSensor);
        if (true == pVolumeSensor->ReadGrams(2)) {
            m_dVolumeAfter = pVolumeSensor->m_dGrams;

            m_dVolumeActual = m_dVolumeBefore - m_dVolumeAfter;

            //LOG(INFO) << INFO_TITTLE() << "Coat after record glue volume:" << pVolumeSensor->m_dGrams <<" ml before" <<m_dVolumeBefore<<" ml";

            m_gSystem.LogInfo(QString("Coat after record glue volume:") + QString::number(pVolumeSensor->m_dGrams) + " ml before" + QString::number(m_dVolumeBefore) + " ml");
            AddProductData(QString("Coat after record glue volume:") + QString::number(pVolumeSensor->m_dGrams) + " ml before" + QString::number(m_dVolumeBefore) + " ml");

        } else {
            //LOG(ERROR) <<ERR_TITTLE()<<"All after get weight data failed.Before "<< m_dVolumeBefore << " ml";
            m_gSystem.LogError(QString("All after get weight data failed.Before ") + QString::number(m_dVolumeBefore) + " ml");
            AddProductData(QString("All after get weight data failed.Before ") + QString::number(m_dVolumeBefore) + " ml");
        }

        double dAlarmDiffVolume = pVolumeSensor->m_Variants.get_VData(Var_CoatVolume_AlarmDiffVolume)->toDouble();

        //LOG(INFO) << INFO_TITTLE() << "Coat check actual:" << m_dTotalBump <<"ml detected:" << m_dVolumeActual<<" ml";
        m_gSystem.LogInfo(QString("Coat check actual:") + QString::number(m_dTotalBump) + "ml detected:" + QString::number(m_dVolumeActual) + " ml");

        AddProductData(QString("Coat check actual:") + QString::number(m_dTotalBump) + "ml detected:" + QString::number(m_dVolumeActual) + " ml");
        if (fabs(m_dTotalBump - m_dVolumeActual) > dAlarmDiffVolume) {
            MQT_THROW_CODESTR(ALARM_COAT_FLUID_CoaingLiquidUsedMisatchDetected, QObject::tr("Coat bump liquid volume %1 ml, tar sensor record %2 ml is out of range %3").arg(m_dTotalBump, 0, 'f', 1).arg(m_dVolumeActual, 0, 'f', 1).arg(dAlarmDiffVolume));
        }
    } else if (iStep == 70) {
        AddProductData(QString("Coat finish at:%1 Spend time:%2S").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz")).arg(m_COAT_CLEAN_TIME.GetTime()));
        m_gSystem.EvtLog(QObject::tr("COAT"), QObject::tr("WaferID:%1,COAT Finished").arg(m_gSystem.GetCoatWaferId().c_str()));
    }
    else if (iStep == 100)
    {
        ReleaseRes(SM_COAT, kResCoat);
    }
    CProcess_CoatClean::all_action_after(iActionType, iStep);
}

void CProcess_Coat::Wait_Spindle_finish()
{
    CMotorDriver* pMotorDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pMotorDriver);

    pMotorDriver->wait_move_finished(MOTORE_COAT_SPINDLE);
}

void CProcess_Coat::Bump_Test(int iStep)
{
    if (iStep == 150) SpindleUp(false);
    if (iStep == 155) CoatArmUp(true);
    if (iStep == 160) MoveCoatWaitPos();
    if (iStep == 165) MoveCleanWaitPos();
    if (iStep == 170) {
        CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
        pDriver->setdefaultspeed(MOTORE3_COAT_BUMP);
        Bump();
    }
    if (iStep == 190) {
        m_gSystem.m_SysMsg.SetMsg
        (QObject::trUtf8("Bump coat fluid finished"));
    }
}

void CProcess_Coat::Spary_Test(int iStep)
{
    if (iStep == 150) SpindleUp(false);
    if (iStep == 155) CoatArmUp(true);
    if (iStep == 160)MoveCoatWaitPos();
    if (iStep == 165)MoveCleanWaitPos();
    if (iStep == 170) {
        CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
        Q_ASSERT(pDriver);
        pDriver->setio(IO_OUTPUT, OUT_COAT_CLEANING_GLUE_WATER, TRUE);
    }
    if (iStep == 175)Spray(true);
    if (iStep == 176) {
        MSleep(1000);
    }
    if (iStep == 180) {
        CoatArmUp(false);
    }
    if (iStep == 185) {
        CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
        Q_ASSERT(pDriver);
        pDriver->setio(IO_OUTPUT, OUT_COAT_CLEANING_GLUE_WATER, FALSE);
    }
    if (iStep == 190) {
        m_gSystem.m_SysMsg.SetMsg(QObject::trUtf8("Spary coat fluid finished"));
    }
}

void CProcess_Coat::ReplaceCoatFluid(int iStep)
{
    if (iStep == 1) SpindleUp(false);
    if (iStep == 5) CoatArmUp(true);
    if (iStep == 10)MoveCoatWaitPos();
    if (iStep == 15)MoveCleanWaitPos();
    if (iStep == 16) {
        CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
        Q_ASSERT(pDriver);
        pDriver->setio(IO_OUTPUT, OUT_COAT_CLEANING_GLUE_WATER, TRUE);
        pDriver->setdefaultspeed(MOTORE3_COAT_BUMP);
        m_dReplaceFluidVolume = m_Variants.get_VData(Var_Coat_ReplaceFluidVolume)->toDouble();
    }

    if (iStep >= 20 && iStep < 40) {
        if (m_dReplaceFluidVolume > 0) {
            double dMaxVolume = m_Variants.get_VData(Var_Coat_Bump_Max_ID)->toDouble();
            double dZeroVolume = m_Variants.get_VData(Var_Coat_Spay_ZeroOffset_ID)->toDouble();
            double dPerMaxVolume = dMaxVolume - dZeroVolume;
            if (m_dReplaceFluidVolume > dPerMaxVolume) {
                Bump(dPerMaxVolume, m_dSingleBump);
                m_dReplaceFluidVolume -= dPerMaxVolume;

                Spray(true);
            } else {
                Bump(m_dReplaceFluidVolume, m_dSingleBump);
                Spray(true);
                m_dReplaceFluidVolume = -1;
            }
        }
    }

    if (iStep == 50) {
        MSleep(1000);
    }
    if (iStep == 60) {
        CoatArmUp(false);
    }
    if (iStep == 80) {
        CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
        Q_ASSERT(pDriver);
        pDriver->setio(IO_OUTPUT, OUT_COAT_CLEANING_GLUE_WATER, FALSE);
    }

    if (iStep == 200) {

        CCoatVolumeSensor* pVolumeSensor = GET_CARD(COATVOLUME_SENSOR, CCoatVolumeSensor);
        QString strCurDate = QDate::currentDate().toString("yyyy-MM-dd");
        pVolumeSensor->m_Variants.get_VData(Var_CoatVolume_ReplaceDate)->setValue(strCurDate);
        LOG_INFO("Replace Fluid");
        CProcess_Trans* ptrans = GET_PROCESS(PROCESS_TRANS, SM_TRANS,
                                             CProcess_Trans);
        Q_ASSERT(ptrans);
        if (ptrans->m_pSlotCoat && !ptrans->m_pSlotCoat->bNeedCheckCoat) {
            ptrans->m_pSlotCoat->bNeedCheckCoat = true;
            ptrans->m_pSlotCoat->bNeedCheckCut = true;
            QString info = QObject::trUtf8("Replace coat fluid finished") + " " + QObject::tr("After coat will check coat quality");
            m_gSystem.m_SysMsg.SetMsg(info);

            m_gSystem.AddProductInfo(PROCESS_COAT, "Replace coat fluid finished " + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));

        } else {
            m_gSystem.m_SysMsg.SetMsg
            (QObject::trUtf8("Replace coat fluid finished"));
        }
    }
}

void CProcess_Coat::ReadWriteCleanTable(bool bWrite)
{
    QString sFilePath = CMakePath::make_config_path(PATH_CLEANTABLE_COAT);
    if (bWrite) {
        WriteCleanTable(sFilePath, m_vCleanTableStep);
    } else {
        ReadCleanTable(sFilePath, m_vCleanTableStep);
    }
}

void CProcess_Coat::ReadWriteCleanCoatLiquid(bool bWrite)
{
    QString sFilePath = CMakePath::make_config_path(PATH_CLEANTABLE_COATFLUID);
    if (bWrite) {
        WriteCleanTable(sFilePath, m_vCleanTableRing);
    } else {
        ReadCleanTable(sFilePath, m_vCleanTableRing);
    }
}

void CProcess_Coat::Init_Pos()
{
    //关闭喷头
    nozzeloperator_coat(INIT_COAT);

    //回原点
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    SpindleStop();
    MoveSpindleInitPos(false, false);

    MoveCleanWaitPos();

    CoatArmUp(true);
    MoveCoatWaitPos();
    CoatArmUp(false);
    
    pDriver->setio(IO_OUTPUT, OUT_COAT_CLEANING_GLUE_WATER, TRUE);

    CloseProtect(true);   //防水帘

    MoveSpindleInitPos(false, true);
}

/*!
 * \brief CProcess_Coat::nozzeloperator_coat
 * 喷头操作
 * INIT_COAT 关闭喷头
 * \param iType
 */
void  CProcess_Coat::nozzeloperator_coat(int32_t iType)
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    switch (iType) {
    case COAT:
    case COAT_M:
        break;
    case DRY://SPIN仅旋转
        SetWaferHasWafer(false);
        break;
    case WATER_SOAK:
        //水润
        pDriver->setio(IO_OUTPUT, OUT_COAT_TWOFLUID_WATER, true);
        SetWaferHasWafer(true);
        break;
    case FCLN://洗铁环
        pDriver->setio(IO_OUTPUT, OUT_COAT_WASHRING_WATER, TRUE);
        SetWaferHasWafer(true);
        break;
    case FCLN_DRY:
        pDriver->setio(IO_OUTPUT, OUT_COAT_WASHRING_WATER, TRUE);
        SetWaferHasWafer(true);
        break;
    case DRY_M:
        //TODO:用来清洁平台
        //pDriver->setio(IO_OUTPUT,OUT_COAT_AIR, true);    //开风
        pDriver->setio(IO_OUTPUT, OUT_COAT_TWOFLUID_AIR, true);
        pDriver->setio(IO_OUTPUT, OUT_COAT_ELECT_AIR, true);
        SetWaferHasWafer(false);
        break;
    case WASH:
        pDriver->setio(IO_OUTPUT, OUT_COAT_TWOFLUID_WATER, true);
        pDriver->setio(IO_OUTPUT, OUT_COAT_TWOFLUID_AIR, true);
        pDriver->setio(IO_OUTPUT, OUT_COAT_ELECT_AIR, true);
        SetWaferHasWafer(true);
        break;
    default:
    {
        //摆臂上的喷头全部归位
        pDriver->setio(IO_OUTPUT, OUT_COAT_ELECT_AIR, FALSE);
        pDriver->setio(IO_OUTPUT, OUT_COAT_TWOFLUID_AIR, FALSE);
        pDriver->setio(IO_OUTPUT, OUT_COAT_TWOFLUID_WATER, FALSE);
        pDriver->setio(IO_OUTPUT, OUT_COAT_WASHRING_WATER, FALSE);
    }
    break;
    }
}

/*!
 * \brief CProcess_Coat::CoatArmUp
 * 摆臂升起，涂覆摆臂在必须在升起状态才能在工作区域中使用
 * \param bUp
 * bUp=true 升起
 */
void CProcess_Coat::CoatArmUp(bool bUp)
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    if (bUp) {
        pDriver->set_cylinder(OUT_COAT_ARM_UP,
                              IN_COAT_ARM_UP_FORWARD);
    } else {
        pDriver->set_cylinder(OUT_COAT_ARM_UP,
                              IN_COAT_ARM_DOWN_BACK, FALSE);
    }
}

void CProcess_Coat::CleanArmUp(bool bUp)
{
    UNUSED(bUp);
}

void  CProcess_Coat::SpindleUp(bool bUp, bool bAfterChecked/*=false*/)
{
    //do nothing
    /*CMotorDriver* pDriver=GET_CARD(MOTOR_DRIVER,CMotorDriver);
    Q_ASSERT(pDriver);
    int iTimeOut= m_Variants.get_VData(Var_CoatClean_Spindle_Move_TimeOut)
            ->toInt();
    if(false == bAfterChecked){
        if (bUp)
        {
            pDriver->set_cylinder(OUT_COAT_SPINDLE_UPDOWN,
                                  IN_COAT_SPINDLE_UP_FORWARD,true,iTimeOut);
        }
        else
        {
            pDriver->set_cylinder(OUT_COAT_SPINDLE_UPDOWN,
                                  IN_COAT_SPINDLE_DOWN_BACK,FALSE,iTimeOut);
        }
    }else{
        if (bUp)
        {
            pDriver->set_cylinder(OUT_COAT_SPINDLE_UPDOWN,
                -1,TRUE,iTimeOut);
        }
        else
        {
            pDriver->set_cylinder(OUT_COAT_SPINDLE_UPDOWN,
                -1,FALSE,iTimeOut);
        }
    }*/
}
//
void CProcess_Coat::SpindleUpCheck(bool bUp)
{
    //#ifdef __SIMULATOR__
    //    return;
    //#endif // __SIMULATOR__
    //    CMotorDriver_Base* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver_Base);
    //    Q_ASSERT(pDriver);
    //    int iTimeOut = m_Variants.get_VData(Var_CoatClean_Spindle_Move_TimeOut)
    //        ->toInt();
    //    if (bUp) {
    //        pDriver->check_cylinder(OUT_COAT_SPINDLE_UPDOWN,
    //                                IN_COAT_SPINDLE_UP_FORWARD, true, iTimeOut);
    //    } else {
    //        pDriver->check_cylinder(OUT_COAT_SPINDLE_UPDOWN,
    //                                IN_COAT_SPINDLE_DOWN_BACK, false, iTimeOut);
    //    }
}

//bool CProcess_Coat::isSpindleUp()
//{
//    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
//    Q_ASSERT(pDriver);
//    return pDriver->get_iodata(IO_INPUT, IN_COAT_SPINDLE_UP_FORWARD, true);
//}

void CProcess_Coat::MoveCleanWaitPos()
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    double dHeadCleanWaitPos = m_Variants.get_VData(Var_Coat_Clean_Arm_WaitPos_ID)
        ->toDouble();
    pDriver->stopmove(MOTORE1_COAT_CLEAN_ARM);
    pDriver->absmove(MOTORE1_COAT_CLEAN_ARM, dHeadCleanWaitPos, TRUE);
}

void CProcess_Coat::MoveCleanPreparePos()
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    double dCleanPreparePos = m_Variants.get_VData(Var_Coat_Clean_Arm_PerparePos_ID)
        ->toDouble();
    pDriver->absmove(MOTORE1_COAT_CLEAN_ARM, dCleanPreparePos, TRUE);
}

void CProcess_Coat::MoveCleanEagePos()
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    double dCleanEagePos = m_Variants.get_VData(Var_Coat_Clean_Arm_EagePos_ID)
        ->toDouble();
    pDriver->absmove(MOTORE1_COAT_CLEAN_ARM, dCleanEagePos, TRUE);
}

void CProcess_Coat::MoveCleanCenterPos()
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    double dcleancenterpos = m_Variants.get_VData(Var_Coat_Clean_Arm_CenterPos_ID)
        ->toDouble();
    pDriver->absmove(MOTORE1_COAT_CLEAN_ARM, dcleancenterpos, TRUE);
}

void CProcess_Coat::MoveCleanRingPos()
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    double dcleanringpos = m_Variants.get_VData(Var_Coat_Clean_Arm_RingPos_ID)
        ->toDouble();
    pDriver->absmove(MOTORE1_COAT_CLEAN_ARM, dcleanringpos, TRUE);
}

void CProcess_Coat::MoveCleanCyclic()
{
    CMotorDriver_Base* pAcsMotorDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver_Base);
    Q_ASSERT(pAcsMotorDriver);
    double PT[3], PTSPD[3];
    PT[0] = m_Variants.get_VData(Var_Coat_Clean_Arm_EagePos_ID)
        ->toDouble();
    PT[1] = m_Variants.get_VData(Var_Coat_Clean_Arm_CenterPos_ID)
        ->toDouble();
    PT[2] = PT[0];
    PTSPD[0] = m_Variants.get_VData(Var_Coat_Arm_Speed_ID)
        ->toDouble();
    PTSPD[2] = PTSPD[1] = PTSPD[0];
    pAcsMotorDriver->MultiPointMove(MOTORE1_COAT_CLEAN_ARM, PT, 3, 1, PTSPD);
}

/*!
 * \brief CProcess_Coat::MoveCoatWaitPos
 * 摆臂移动到准备位置
 */
void CProcess_Coat::MoveCoatWaitPos()
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    double dHeadCoatWaitPos = m_Variants.get_VData(Var_Coat_Arm_WaitPos_ID)
        ->toDouble();
    pDriver->stopmove(MOTORE2_COAT_COAT_ARM);
    pDriver->absmove(MOTORE2_COAT_COAT_ARM, dHeadCoatWaitPos, TRUE);

}

/*!
 * \brief CProcess_Coat::MoveCoatPreparePos
 * 移动到涂敷准备位置
 */
void CProcess_Coat::MoveCoatPreparePos()
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    double dHeadCoatPreparePos = m_Variants.get_VData(Var_Coat_Arm_PerparePos_ID)
        ->toDouble();
    pDriver->absmove(MOTORE2_COAT_COAT_ARM, dHeadCoatPreparePos, TRUE);
}

void CProcess_Coat::MoveCoatEagePos()
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    double dHeadCoatEagePos = m_Variants.get_VData(Var_Coat_Arm_EagePos_ID)
        ->toDouble();
    pDriver->absmove(MOTORE2_COAT_COAT_ARM, dHeadCoatEagePos, TRUE);
}

//涂敷臂移动到中心
void CProcess_Coat::MoveCoatCenterPos(bool bOffset)
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    double dHeadCoatCenterPos = m_Variants.get_VData(Var_Coat_Arm_CenterPos_ID)
        ->toDouble();

    if (bOffset) {
        pDriver->absmove(MOTORE2_COAT_COAT_ARM, dHeadCoatCenterPos + 5, TRUE);
    }

    pDriver->absmove(MOTORE2_COAT_COAT_ARM, dHeadCoatCenterPos, TRUE);
}

void CProcess_Coat::MoveCoatMultiSpd(bool bWait)
{
    CMotorDriver_ACS* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver_ACS);
    Q_ASSERT(pDriver);
    double dHeadCoatCenterPos = m_Variants.get_VData(Var_Coat_Arm_CenterPos_ID)
        ->toDouble();
    double dHeadCoatEagePos = m_Variants.get_VData(Var_Coat_Arm_EagePos_ID)
        ->toDouble();

    //中心速度
    double dCoatMCenterSpeed = m_Variants.get_VData(Var_Coat_M_CenterSpeed_ID)
        ->toDouble();
    double dCoatMEageSpeed = m_Variants.get_VData(Var_Coat_M_EageSpeed_ID)
        ->toDouble();
    double dPoint[2] = { dHeadCoatCenterPos,dHeadCoatEagePos };
    double dVel[2] = { dCoatMCenterSpeed,dCoatMEageSpeed };

    //多点运动
    pDriver->MultiPointMove(MOTORE2_COAT_COAT_ARM, dPoint, 2, FALSE, dVel);
    if (bWait) {
        pDriver->wait_move_finished(MOTORE2_COAT_COAT_ARM);
    }
}

void CProcess_Coat::MoveCoatMultiSpd(bool bWait, double dCenterSpeed, double dEageSpeed)
{
    CMotorDriver_ACS* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver_ACS);
    Q_ASSERT(pDriver);
    double dHeadCoatCenterPos = m_Variants.get_VData(Var_Coat_Arm_CenterPos_ID)
        ->toDouble();
    double dHeadCoatEagePos = m_Variants.get_VData(Var_Coat_Arm_EagePos_ID)
        ->toDouble();

    //中心速度
    double dCoatMCenterSpeed = dCenterSpeed;
    double dCoatMEageSpeed = dEageSpeed;
    double dPoint[2] = { dHeadCoatCenterPos,dHeadCoatEagePos };
    double dVel[2] = { dCoatMCenterSpeed,dCoatMEageSpeed };

    //多点运动
    pDriver->MultiPointMove(MOTORE2_COAT_COAT_ARM, dPoint, 2, FALSE, dVel);
    if (bWait) {
        pDriver->wait_move_finished(MOTORE2_COAT_COAT_ARM);
    }
}

void CProcess_Coat::MoveCleanProtectionBlockPos()
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    double dcleancenterpos = m_Variants.get_VData(Var_Coat_Clean_ProtectionBlockPos)
        ->toDouble();
    pDriver->absmove(MOTORE1_COAT_CLEAN_ARM, dcleancenterpos, TRUE);
}

/*!
 * \brief CProcess_Coat::Bump
 * 吸胶,一次性吸够
 */
void CProcess_Coat::Bump()
{
    //CMotorDriver* pDriver=GET_CARD(MOTOR_DRIVER,CMotorDriver);
 /*   if (!pDriver->get_iodata(IO_INPUT,IN_COAT_GLUE_ALM,true))
    {
        MQT_THROW_STR(QObject::trUtf8("Glue container is Empty "));
    }
*/
/*double dglueML=m_Variants.get_VData(Var_Coat_Spay_Num_ID)
        ->toDouble();*/
    double dMaxVolume = m_Variants.get_VData(Var_Coat_Bump_Max_ID)
        ->toDouble();
    Bump(dMaxVolume, m_dSingleBump, true);
    //double dglueZero=m_Variants.get_VData(Var_Coat_Spay_ZeroOffset_ID)
    //        ->toDouble();

    //nozzeloperator_coat(COAT);
    //pDriver->absmove(MOTORE3_COAT_BUMP,dglueZero,true);

    ////关闭气阈和水阈
    //nozzeloperator_coat(INIT_COAT);
    //pDriver->absmove(MOTORE3_COAT_BUMP,dglueML+dglueZero,true);
}

void CProcess_Coat::Bump(double dSpayNum, double& dActualBump, bool bZeroFirst/*=true*/)
{
    dActualBump = 0;
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    /*   if (!pDriver->get_iodata(IO_INPUT,IN_COAT_GLUE_ALM,true))
       {
           MQT_THROW_STR(QObject::trUtf8("Glue container is Empty "));
       }
   */
    double dglueML = dSpayNum;
    double dglueZero = m_Variants.get_VData(Var_Coat_Spay_ZeroOffset_ID)
        ->toDouble();

    nozzeloperator_coat(COAT);
    if (bZeroFirst)
        pDriver->absmove(MOTORE3_COAT_BUMP, dglueZero, true);

    //关闭气阈和水阈
    nozzeloperator_coat(INIT_COAT);

    double dCurVolume = pDriver->get_posdata(MOTORE3_COAT_BUMP, true);
    double dTargetVolume = dglueML + dglueZero;
    //小于需要的胶量，则抽胶
    if (dCurVolume < dTargetVolume) {
        double dMaxVolume = m_Variants.get_VData(Var_Coat_Bump_Max_ID)->toDouble();
        if (dglueML + dglueZero > dMaxVolume) {
            MQT_THROW_CODESTR(ALARM_COAT_FLUID_BumpParam_ERROR, QObject::trUtf8("Coat liquid volume greater than max valume"));
        }

        dActualBump = dTargetVolume - dCurVolume;
        pDriver->absmove(MOTORE3_COAT_BUMP, dTargetVolume, true);
    }
}

void CProcess_Coat::BumpNoWait(double dSpayNum, double& dRetNum, double& dActualBump)
{
    dActualBump = 0;
    if (DEQUAL0(dSpayNum))return;

    double dglueML = dSpayNum;
    double dglueZero = m_Variants.get_VData(Var_Coat_Spay_ZeroOffset_ID)
        ->toDouble();
    Q_ASSERT(dSpayNum > -0.001);

    //默认认为已经吸起来了，则不再往外面推了，直接往上吸
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    double curVolume = pDriver->get_posdata(MOTORE3_COAT_BUMP, true);

    //目标位置
    double dTargetVolume = curVolume + dSpayNum;
    if (curVolume < dglueZero) {
        //如果小于下限，则要把下限也加上
        dTargetVolume = curVolume + dSpayNum + dglueZero;
    }
    //dRetNum = curVolume; 
    double dMaxVolume = m_Variants.get_VData(Var_Coat_Bump_Max_ID)
        ->toDouble();

    //if(DEQUAL0(dMaxVolume-dTargetVolume))
    //	return;

    //如果大于最大值，则以最大值为准
    if (dTargetVolume > dMaxVolume)
        dTargetVolume = dMaxVolume;


    //运动，不等待结束，后面等待结束
    pDriver->absmove(MOTORE3_COAT_BUMP, dTargetVolume, false);

    dRetNum = dTargetVolume;
    dActualBump = dTargetVolume - curVolume;
}

void CProcess_Coat::WaitBumpFinished(double dSpayNum, double dTargetNum)
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    pDriver->wait_move_finished(MOTORE3_COAT_BUMP);

    double curVolume = pDriver->get_posdata(MOTORE3_COAT_BUMP, true);
    if (!INRANGE(curVolume - dTargetNum, -0.01, 0.01)) {
        MQT_THROW_CODESTR(ALARM_COAT_FLUID_BumpError, QObject::trUtf8("Bump Coat Volume:%1 Failed Actual:%2")
                          .arg(dTargetNum).arg(curVolume));
    }
}

/*!
 * \brief CProcess_Coat::Spray
 * 喷胶
 * \param bWaitFinished
 * 等待完成
 */
void CProcess_Coat::Spray(bool bWaitFinished)
{
    //喷胶速度
    //double dglueSpd=m_Variants.get_VData(Var_Coat_Spay_Spd_ID)
    //        ->toDouble();

    double dMaxVolume = m_Variants.get_VData(Var_Coat_Bump_Max_ID)
        ->toDouble();

    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    double dglueSpd = pDriver->get_defaultSpd(MOTORE3_COAT_BUMP);
    Spray(dMaxVolume, bWaitFinished, dglueSpd);
}

/*!
 * \brief CProcess_Coat::Spray
 * 喷胶
 * \param bWaitFinished
 * 等待完成
 */
void CProcess_Coat::Spray(double dSpayVolume, bool bWaitFinished, double dSpaySpd)
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);

    //喷胶速度
    pDriver->setspeed(MOTORE3_COAT_BUMP, dSpaySpd, 0, 0, 0, 0);

    nozzeloperator_coat(COAT);
    double dglueZero = m_Variants.get_VData(Var_Coat_Spay_ZeroOffset_ID)
        ->toDouble();

    double curVolume = pDriver->get_posdata(MOTORE3_COAT_BUMP, true);
    double targetVolume = curVolume - dSpayVolume;

    if (targetVolume < dglueZero)
        targetVolume = dglueZero;

    //回原点，即推出去
    pDriver->absmove(MOTORE3_COAT_BUMP, targetVolume, bWaitFinished);
    if (bWaitFinished) {
        nozzeloperator_coat(INIT_COAT);
    }
}

/*!
 * \brief CProcess_Coat::Spray_prepare
 * 预喷胶
 */
void CProcess_Coat::Spray_prepare()
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);

    double dgluePrepare = m_Variants.get_VData(Var_Coat_Spay_Pre_Num_ID)
        ->toDouble();
    double dglueSpd = m_Variants.get_VData(Var_Coat_Spay_Spd_ID)
        ->toDouble();

    Spray_prepare(dgluePrepare, dglueSpd, 0);
}

void CProcess_Coat::Spray_prepare(double dSpayVolume, double dSpaySpd, int iWaitMs)
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);

    double dglueSpd = dSpaySpd;
    if (DEQUAL0(dSpaySpd)) {
        pDriver->setdefaultspeed(MOTORE3_COAT_BUMP);
    } else {
        pDriver->setspeed(MOTORE3_COAT_BUMP, dglueSpd, 0, 0, 0, 0);
    }

    nozzeloperator_coat(COAT);
    //预唝胶量
    double dgluePrepare = m_Variants.get_VData(Var_Coat_Spay_Pre_Num_ID)
        ->toDouble();

    dgluePrepare = dSpayVolume;

    //预唝胶
    pDriver->relmove(MOTORE3_COAT_BUMP, -fabs(dgluePrepare), true);
    nozzeloperator_coat(INIT_COAT);
    if (iWaitMs > 0)
        MSleep(iWaitMs);
}

void CProcess_Coat::SprayPrepareNoWait(double dPreSpayVolume)
{
    if (DEQUAL0(dPreSpayVolume))return;

    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);

    double dglueZero = m_Variants.get_VData(Var_Coat_Spay_ZeroOffset_ID)
        ->toDouble();

    double dCurVolume = pDriver->get_posdata(MOTORE3_COAT_BUMP, true);
    double dTargetVolume = dCurVolume - fabs(dPreSpayVolume);
    if (dTargetVolume < dglueZero) {
        dTargetVolume = dglueZero;
    }

    //预唝胶
    pDriver->absmove(MOTORE3_COAT_BUMP, dTargetVolume, false);
}

void CProcess_Coat::WaitSparyPrepareFinished(double dPreSpayVolume)
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    pDriver->wait_move_finished(MOTORE3_COAT_BUMP);
}

void CProcess_Coat::SetdefaultSpeed(bool bSetSpindle)
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    if (bSetSpindle) {
        pDriver->setdefaultspeed(MOTORE_COAT_SPINDLE);
    }
    pDriver->setdefaultspeed(MOTORE1_COAT_CLEAN_ARM);
    pDriver->setdefaultspeed(MOTORE2_COAT_COAT_ARM);
    pDriver->setdefaultspeed(MOTORE3_COAT_BUMP);
}

void CProcess_Coat::SpindleSpin(double drpm)
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    pDriver->startmove(MOTORE_COAT_SPINDLE, fabs(drpm * 360 / 60));
}

void CProcess_Coat::SpindleStop()
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    pDriver->stopmove(MOTORE_COAT_SPINDLE);
    MSleep(3000);
    pDriver->setdefaultspeed(MOTORE_COAT_SPINDLE);
}

//主轴到工艺初始位置
void CProcess_Coat::MoveSpindleInitPos(bool bhome/*=true*/, bool bWait/*=true*/)
{
    if (isSpindleHomed()) return;
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);

    double dSpindleAfterCoatPos = m_Variants.get_VData(Var_Coat_Spindle_InitPos_ID)->toDouble();
    if (bhome) {
        int curPos = (int)pDriver->get_posdata(MOTORE_COAT_SPINDLE, true);
        CMotor* pMotor = static_cast<CMotor*>(pDriver->FindChannel(MOTORE_COAT_SPINDLE));
        bool bEnabled = pMotor->m_Variants.get_VData(Var_Motor_Axis_Enabled)->toBool();
        bool bHomed = pMotor->m_Variants.get_VData(Var_Motor_Hashome_ID)->toBool();
        if (!bEnabled || !bHomed)
        {
            //如果掉使能或者未回原,则直接回原点
            pDriver->home(MOTORE_COAT_SPINDLE, true);
        }
        else if (fabs(curPos) > 360) {
            //Offset 67 + 20
            double dTargetPos = -20;
            double calcPos = curPos % 360;

            double distance1 = calcPos - (-360 + dTargetPos);
            double distance2 = calcPos - (0 + dTargetPos);
            double distance3 = calcPos - (360 + dTargetPos);
            double calcOffset = distance1;

            if (fabs(distance2) < fabs(calcOffset)) {
                calcOffset = distance2;
            }
            if (fabs(distance3) < fabs(calcOffset)) {
                calcOffset = distance3;
            }

            curPos -= calcOffset;

            pDriver->setspeed(MOTORE_COAT_SPINDLE, 200, 0, 0, 0, 0);
            pDriver->absmove(MOTORE_COAT_SPINDLE, curPos, true);
            pDriver->home(MOTORE_COAT_SPINDLE, true);
        }
        else
        {
            //如果已经回原了
            //do nothing
        }
    }
    pDriver->setdefaultspeed(MOTORE_COAT_SPINDLE);
    pDriver->absmove(MOTORE_COAT_SPINDLE, dSpindleAfterCoatPos, bWait);
}

void CProcess_Coat::Wait_Spray_Finish()
{

    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    while (!pDriver->is_move_finished(MOTORE3_COAT_BUMP)) {
        MSleep(10);
        /*        if (pDriver->get_iodata(IO_INPUT,IN_COAT_BUBBLE_ALM,true))
               {
                   MQT_THROW_STR(QObject::trUtf8("Coat Glue bubble alarm"));
               }
               */
    }
}

/*!
 * \brief CProcess_Coat::CloseProtect
 * 放水帘关闭控制
 * \param bClose
 */
void CProcess_Coat::CloseProtect(BOOL bClose, bool bWait)
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    if (bClose) {
        pDriver->set_cylinder(OUT_COAT_CURTAIN_OPENED, IN_COAT_CURTAIN_CLOSED, FALSE);
    } else {
        pDriver->set_cylinder(OUT_COAT_CURTAIN_OPENED, IN_COAT_CURTAIN_OPENED, TRUE);
    }
}

void CProcess_Coat::CoatCenterWait()
{
    dword_t dwait = m_Variants.get_VData(Var_Coat_M_CenterWaitTime_ID)
        ->toInt();
    MSleep(dwait);
}

void CProcess_Coat::CoatCenterWait(double dCenterWait)
{
    MSleep(dCenterWait);
}

/*!
 * \brief CProcess_Coat::Wait_CoatArm_finish
 * 等待手臂运行结束
 */
void CProcess_Coat::Wait_CoatArm_finish()
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    pDriver->wait_move_finished(MOTORE2_COAT_COAT_ARM);
    pDriver->setdefaultspeed(MOTORE2_COAT_COAT_ARM);
}

void CProcess_Coat::Wait_CleanArm_finish()
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    pDriver->wait_move_finished(MOTORE1_COAT_CLEAN_ARM);
    pDriver->setdefaultspeed(MOTORE1_COAT_CLEAN_ARM);
}

void CProcess_Coat::CheckVaccum()
{
    if (m_gSystem.IsSimulator())return;

    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    if (!pDriver->get_iodata(IO_OUTPUT, OUT_COAT_VACUUM, true)) {
        MQT_THROW_CODESTR(ALARM_COAT_CoatVacuumOFF, QObject::trUtf8("Vacuum %1 is Off,while coating")
                          .arg(pDriver->getioname(IO_OUTPUT, OUT_COAT_VACUUM)));
    }
    m_gSystem.VacuumLeakCheck(OUT_COAT_VACUUM, IN_COAT_VACUUM);
}

//检查水压
void CProcess_Coat::CheckWater()
{
    CMotorDriver* pDriver = GET_CARD(MOTOR_DRIVER, CMotorDriver);
    Q_ASSERT(pDriver);
    if (pDriver->get_iodata(IO_INPUT, IN_WATER_PRESSURE, true)) {
        MQT_THROW_CODESTR(ALARM_COAT_CoatWaterPressureLow, QObject::trUtf8("water press is Low,while coating"));
    }
}

void CProcess_Coat::SetWaferHasWafer(bool bHas)
{
    CProcess_Trans* ptrans = GET_PROCESS(PROCESS_TRANS, SM_TRANS,
                                         CProcess_Trans);
    
    QMutexLocker(&ptrans->m_slotMutex);
    if (ptrans->m_pSlotCoat)
    {
        ptrans->m_pSlotCoat->bHasWater = bHas;
    }
}
