| | |
| | | namespace SERVO { |
| | | CMaster* g_pMaster = NULL; |
| | | |
| | | unsigned __stdcall DispatchThreadFunction(LPVOID lpParam) |
| | | { |
| | | if (g_pMaster != NULL) { |
| | | return g_pMaster->DispatchProc(); |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | unsigned __stdcall ReadBitsThreadFunction(LPVOID lpParam) |
| | | { |
| | | if (g_pMaster != NULL) { |
| | |
| | | |
| | | CMaster::CMaster() |
| | | { |
| | | m_listener = {nullptr, nullptr, nullptr, nullptr, nullptr}; |
| | | m_listener = {}; |
| | | m_bDataModify = FALSE; |
| | | m_hEventReadBitsThreadExit[0] = ::CreateEvent(NULL, TRUE, FALSE, NULL); |
| | | m_hEventReadBitsThreadExit[1] = ::CreateEvent(NULL, TRUE, FALSE, NULL); |
| | | m_hReadBitsThreadHandle = nullptr; |
| | | m_nReadBitsThreadAddr = 0; |
| | | m_hDispatchEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); |
| | | m_hEventDispatchThreadExit[0] = ::CreateEvent(NULL, TRUE, FALSE, NULL); |
| | | m_hEventDispatchThreadExit[1] = ::CreateEvent(NULL, TRUE, FALSE, NULL); |
| | | m_hDispatchThreadHandle = nullptr; |
| | | m_nDispatchThreadAddr = 0; |
| | | m_ullStartTime = 0; |
| | | m_ullRunTime = 0; |
| | | m_state = MASTERSTATE::READY; |
| | | m_pActiveRobotTask = nullptr; |
| | | InitializeCriticalSection(&m_criticalSection); |
| | | } |
| | | |
| | | CMaster::~CMaster() |
| | | { |
| | | for (auto item : m_listEquipment) { |
| | | delete item; |
| | | } |
| | | m_listEquipment.clear(); |
| | | |
| | | if (m_hEventReadBitsThreadExit[0] != nullptr) { |
| | | ::CloseHandle(m_hEventReadBitsThreadExit[0]); |
| | | m_hEventReadBitsThreadExit[0] = nullptr; |
| | |
| | | ::CloseHandle(m_hEventReadBitsThreadExit[1]); |
| | | m_hEventReadBitsThreadExit[1] = nullptr; |
| | | } |
| | | |
| | | if (m_hDispatchEvent != nullptr) { |
| | | ::CloseHandle(m_hDispatchEvent); |
| | | m_hDispatchEvent = nullptr; |
| | | } |
| | | |
| | | if (m_hEventDispatchThreadExit[0] != nullptr) { |
| | | ::CloseHandle(m_hEventDispatchThreadExit[0]); |
| | | m_hEventDispatchThreadExit[0] = nullptr; |
| | | } |
| | | |
| | | if (m_hEventDispatchThreadExit[1] != nullptr) { |
| | | ::CloseHandle(m_hEventDispatchThreadExit[1]); |
| | | m_hEventDispatchThreadExit[1] = nullptr; |
| | | } |
| | | |
| | | DeleteCriticalSection(&m_criticalSection); |
| | | } |
| | | |
| | | void CMaster::setListener(MasterListener listener) |
| | | { |
| | | m_listener.onEqAlive = listener.onEqAlive; |
| | | m_listener.onEqCimStateChanged = listener.onEqCimStateChanged; |
| | | m_listener.onEqAlarm = listener.onEqAlarm; |
| | | m_listener.onEqVcrEventReport = listener.onEqVcrEventReport; |
| | | m_listener.onEqDataChanged = listener.onEqDataChanged; |
| | | m_listener = listener; |
| | | } |
| | | |
| | | CRobotTask* CMaster::getActiveRobotTask() |
| | | { |
| | | return m_pActiveRobotTask; |
| | | } |
| | | |
| | | int CMaster::init() |
| | |
| | | CVacuumBake* pVacuumBake; |
| | | CAligner* pAligner; |
| | | CBakeCooling* pBakeCooling; |
| | | CMeasurement* pMeasurement; |
| | | |
| | | pPort1 = addLoadPort(0); |
| | | pPort2 = addLoadPort(1); |
| | |
| | | pBonder1 = addBonder(0); |
| | | pBonder2 = addBonder(1); |
| | | pBakeCooling = addBakeCooling(); |
| | | pMeasurement = addMeasurement(); |
| | | |
| | | ASSERT(pEfem); |
| | | ASSERT(pFliper); |
| | |
| | | ASSERT(pBonder1); |
| | | ASSERT(pBonder2); |
| | | ASSERT(pBakeCooling); |
| | | ASSERT(pMeasurement); |
| | | |
| | | pEfem->setPort(0, pPort1); |
| | | pEfem->setPort(1, pPort1); |
| | |
| | | pBonder1->setArm(pArm); |
| | | pBonder2->setArm(pArm); |
| | | pBakeCooling->setArm(pArm); |
| | | pMeasurement->setArm(pArm); |
| | | |
| | | connectEquipments(); |
| | | |
| | |
| | | SetTimer(NULL, 1, 250, (TIMERPROC)MasterTimerProc); |
| | | |
| | | |
| | | // 调度线程 |
| | | m_hDispatchThreadHandle = (HANDLE)_beginthreadex(NULL, 0, SERVO::DispatchThreadFunction, this, |
| | | 0, &m_nDispatchThreadAddr); |
| | | |
| | | |
| | | // 监控bit线程 |
| | | m_hReadBitsThreadHandle = (HANDLE)_beginthreadex(NULL, 0, SERVO::ReadBitsThreadFunction, this, |
| | | 0, &m_nReadBitsThreadAddr); |
| | | |
| | |
| | | int CMaster::term() |
| | | { |
| | | SetEvent(m_hEventReadBitsThreadExit[0]); |
| | | SetEvent(m_hEventDispatchThreadExit[0]); |
| | | ::WaitForSingleObject(m_hEventReadBitsThreadExit[1], INFINITE); |
| | | ::WaitForSingleObject(m_hEventDispatchThreadExit[1], INFINITE); |
| | | |
| | | LOGI("<Master>正在结束程序."); |
| | | for (auto item : m_listEquipment) { |
| | |
| | | } |
| | | saveCache(); |
| | | |
| | | lock(); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | delete m_pActiveRobotTask; |
| | | m_pActiveRobotTask = nullptr; |
| | | } |
| | | unlock(); |
| | | |
| | | for (auto item : m_listEquipment) { |
| | | delete item; |
| | | } |
| | | m_listEquipment.clear(); |
| | | |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int CMaster::start() |
| | | { |
| | | if (m_state != MASTERSTATE::READY) { |
| | | return -1; |
| | | } |
| | | |
| | | setState(MASTERSTATE::STARTING); |
| | | m_ullStartTime = GetTickCount64(); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int CMaster::stop() |
| | | { |
| | | // 运行时间为累加结果,本次停止时刷新; |
| | | if (m_state != MASTERSTATE::RUNNING) { |
| | | return -1; |
| | | } |
| | | |
| | | m_ullRunTime += (GetTickCount64() - m_ullStartTime); |
| | | setState(MASTERSTATE::STOPPING); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | ULONGLONG CMaster::getRunTime() |
| | | { |
| | | if (m_state == MASTERSTATE::RUNNING) |
| | | return m_ullRunTime + (GetTickCount64() - m_ullStartTime); |
| | | else |
| | | return m_ullRunTime; |
| | | } |
| | | |
| | | MASTERSTATE CMaster::getState() |
| | | { |
| | | return m_state; |
| | | } |
| | | |
| | | unsigned CMaster::DispatchProc() |
| | | { |
| | | // 各种机器 |
| | | CLoadPort* pLoadPort1 = (CLoadPort*)getEquipment(EQ_ID_LOADPORT1); |
| | | CLoadPort* pLoadPort2 = (CLoadPort*)getEquipment(EQ_ID_LOADPORT2); |
| | | CFliper* pFliper = (CFliper*)getEquipment(EQ_ID_FLIPER); |
| | | CVacuumBake* pVacuumBack = (CVacuumBake*)getEquipment(EQ_ID_VACUUMBAKE); |
| | | CAligner* pAligner = (CAligner*)getEquipment(EQ_ID_ALIGNER); |
| | | |
| | | ASSERT(pLoadPort1); |
| | | ASSERT(pLoadPort2); |
| | | ASSERT(pFliper); |
| | | ASSERT(pVacuumBack); |
| | | ASSERT(pAligner); |
| | | |
| | | while (1) { |
| | | // 待退出信号或时间到 |
| | | HANDLE hEvents[] = { m_hEventDispatchThreadExit[0], m_hDispatchEvent }; |
| | | int nRet = WaitForMultipleObjects(2, hEvents, FALSE, 1000); |
| | | if (nRet == WAIT_OBJECT_0) { |
| | | break; |
| | | } |
| | | |
| | | |
| | | // 如果状态为STARTING,开始工作并切换到RUNNING状态 |
| | | lock(); |
| | | if (m_state == MASTERSTATE::STARTING) { |
| | | unlock(); |
| | | Sleep(1000); |
| | | setState(MASTERSTATE::RUNNING); |
| | | continue; |
| | | } |
| | | |
| | | |
| | | // 处理完成当前事务后,切换到停止或就绪状态 |
| | | else if (m_state == MASTERSTATE::STOPPING) { |
| | | unlock(); |
| | | Sleep(1000); |
| | | setState(MASTERSTATE::READY); |
| | | continue; |
| | | } |
| | | |
| | | |
| | | // 调度逻辑处理 |
| | | else if (m_state == MASTERSTATE::RUNNING) { |
| | | unlock(); |
| | | // LOGI("调度处理中..."); |
| | | |
| | | lock(); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | unlock(); |
| | | // 检测到当前有正在下午的任务,确保当前任务完成或中止后继续 |
| | | // LOGI("检测到当前有正在下午的任务,确保当前任务完成或中止后继续..."); |
| | | continue; |
| | | } |
| | | |
| | | // LoadPort -> Fliper(G2) |
| | | m_pActiveRobotTask = createTransferTask(pLoadPort1, pFliper); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | std::string strDescription = m_pActiveRobotTask->getDescription(); |
| | | unlock(); |
| | | if (m_listener.onRobotTaskEvent != nullptr) { |
| | | m_listener.onRobotTaskEvent(this, m_pActiveRobotTask, 0); |
| | | } |
| | | LOGI("创建新任务<%s>...", strDescription.c_str()); |
| | | continue; |
| | | } |
| | | |
| | | m_pActiveRobotTask = createTransferTask(pLoadPort2, pFliper); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | std::string strDescription = m_pActiveRobotTask->getDescription(); |
| | | unlock(); |
| | | if (m_listener.onRobotTaskEvent != nullptr) { |
| | | m_listener.onRobotTaskEvent(this, m_pActiveRobotTask, 0); |
| | | } |
| | | LOGI("创建新任务<%s>...", strDescription.c_str()); |
| | | continue; |
| | | } |
| | | |
| | | |
| | | |
| | | // LoadPort -> VacuumBake(G1) |
| | | m_pActiveRobotTask = createTransferTask(pLoadPort1, pVacuumBack); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | std::string strDescription = m_pActiveRobotTask->getDescription(); |
| | | unlock(); |
| | | if (m_listener.onRobotTaskEvent != nullptr) { |
| | | m_listener.onRobotTaskEvent(this, m_pActiveRobotTask, 0); |
| | | } |
| | | LOGI("创建新任务<%s>...", strDescription.c_str()); |
| | | continue; |
| | | } |
| | | |
| | | m_pActiveRobotTask = createTransferTask(pLoadPort2, pVacuumBack); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | std::string strDescription = m_pActiveRobotTask->getDescription(); |
| | | unlock(); |
| | | if (m_listener.onRobotTaskEvent != nullptr) { |
| | | m_listener.onRobotTaskEvent(this, m_pActiveRobotTask, 0); |
| | | } |
| | | LOGI("创建新任务<%s>...", strDescription.c_str()); |
| | | continue; |
| | | } |
| | | |
| | | |
| | | // Fliper(G2) -> Aligner |
| | | // VacuumBake(G1) -> Aligner |
| | | m_pActiveRobotTask = createTransferTask(pFliper, pAligner); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | std::string strDescription = m_pActiveRobotTask->getDescription(); |
| | | unlock(); |
| | | if (m_listener.onRobotTaskEvent != nullptr) { |
| | | m_listener.onRobotTaskEvent(this, m_pActiveRobotTask, 0); |
| | | } |
| | | LOGI("创建新任务<%s>...", strDescription.c_str()); |
| | | continue; |
| | | } |
| | | |
| | | m_pActiveRobotTask = createTransferTask(pVacuumBack, pAligner); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | std::string strDescription = m_pActiveRobotTask->getDescription(); |
| | | unlock(); |
| | | if (m_listener.onRobotTaskEvent != nullptr) { |
| | | m_listener.onRobotTaskEvent(this, m_pActiveRobotTask, 0); |
| | | } |
| | | LOGI("创建新任务<%s>...", strDescription.c_str()); |
| | | continue; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | // Aligner -> Bonder |
| | | |
| | | |
| | | // Bonder -> BakeCooling |
| | | |
| | | |
| | | // BakeCooling ->Measurement |
| | | |
| | | |
| | | // Measurement -> LoadPort |
| | | |
| | | |
| | | |
| | | unlock(); |
| | | |
| | | } |
| | | unlock(); |
| | | } |
| | | |
| | | SetEvent(m_hEventDispatchThreadExit[1]); |
| | | |
| | | |
| | | // _endthreadex(0); |
| | | TRACE("CMaster::DispatchProc 线程退出\n"); |
| | | return 0; |
| | | } |
| | | |
| | |
| | | m_listener.onEqVcrEventReport(this, p, p2); |
| | | } |
| | | }; |
| | | listener.onPreFethedOutJob = [&](void* pEquipment, CJobDataB* pJobDataB) -> BOOL { |
| | | CEquipment* p = (CEquipment*)pEquipment; |
| | | |
| | | |
| | | // 取片,更新当前搬送任务 |
| | | BOOL bOk = FALSE; |
| | | lock(); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | if (m_pActiveRobotTask->getSrcPosition() == p->getID()) { |
| | | CGlass* pGlass = p->getGlassFromSlot(m_pActiveRobotTask->getSrcSlot()); |
| | | if (pGlass != nullptr) { |
| | | CJobDataB* pJobDataBSrc = pGlass->getJobDataB(); |
| | | if (pJobDataBSrc != nullptr |
| | | && pJobDataBSrc->getCassetteSequenceNo() == pJobDataB->getCassetteSequenceNo() |
| | | && pJobDataBSrc->getJobSequenceNo() == pJobDataB->getJobSequenceNo()) { |
| | | bOk = TRUE; |
| | | LOGI("<CMaster>onPreFethedOutJob, 已校验数据一致性."); |
| | | } |
| | | } |
| | | } |
| | | else if (p->getID() == EQ_ID_ARM_TRAY1 || p->getID() == EQ_ID_ARM_TRAY2) { |
| | | bOk = TRUE; |
| | | } |
| | | } |
| | | unlock(); |
| | | |
| | | if (!bOk) { |
| | | LOGE("<CMaster>onPreFethedOutJob, 数据校验失败."); |
| | | } |
| | | |
| | | return bOk; |
| | | |
| | | }; |
| | | listener.onPreStoredJob = [&](void* pEquipment, CJobDataB* pJobDataB, short& slot) -> BOOL { |
| | | CEquipment* p = (CEquipment*)pEquipment; |
| | | |
| | | |
| | | // 放片,更新当前搬送任务 |
| | | BOOL bOk = FALSE; |
| | | lock(); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | if (m_pActiveRobotTask->getTarPosition() == p->getID()) { |
| | | CGlass* pGlass = p->getGlassFromSlot(m_pActiveRobotTask->getTarSlot()); |
| | | if (pGlass == nullptr) { |
| | | bOk = TRUE; |
| | | slot = m_pActiveRobotTask->getTarSlot(); |
| | | LOGI("<CMaster>onPreFethedOutJob, 已校验数据一致性."); |
| | | } |
| | | } |
| | | else if (p->getID() == EQ_ID_ARM_TRAY1 || p->getID() == EQ_ID_ARM_TRAY2) { |
| | | slot = 1; |
| | | bOk = TRUE; |
| | | } |
| | | } |
| | | unlock(); |
| | | |
| | | if (!bOk) { |
| | | LOGE("<CMaster>onPreFethedOutJob, 数据校验失败."); |
| | | } |
| | | |
| | | return bOk; |
| | | |
| | | }; |
| | | listener.onDataChanged = [&](void* pEquipment, int code) -> void { |
| | | m_bDataModify = TRUE; |
| | | CEquipment* p = (CEquipment*)pEquipment; |
| | | if (m_listener.onEqDataChanged != nullptr) { |
| | | m_listener.onEqDataChanged(this, p, 0); |
| | | } |
| | | |
| | | // 取放片,更新当前搬送任务 |
| | | if (code == EDCC_FETCHOUT_JOB) { |
| | | lock(); |
| | | if (m_pActiveRobotTask != nullptr && m_pActiveRobotTask->getSrcPosition() == p->getID()) { |
| | | m_pActiveRobotTask->fetchOut(); |
| | | LOGI("开始取片..."); |
| | | } |
| | | unlock(); |
| | | } |
| | | else if (code == EDCC_STORED_JOB) { |
| | | lock(); |
| | | if (m_pActiveRobotTask != nullptr && m_pActiveRobotTask->getTarPosition() == p->getID()) { |
| | | m_pActiveRobotTask->stored(); |
| | | m_pActiveRobotTask->completed(); |
| | | LOGI("放片完成..."); |
| | | |
| | | // 完成此条搬送任务,但要把数据和消息上抛应用层 |
| | | unlock(); |
| | | |
| | | |
| | | |
| | | lock(); |
| | | delete m_pActiveRobotTask; |
| | | m_pActiveRobotTask = nullptr; |
| | | |
| | | if (m_listener.onRobotTaskEvent != nullptr) { |
| | | m_listener.onRobotTaskEvent(this, m_pActiveRobotTask, 0); |
| | | } |
| | | } |
| | | unlock(); |
| | | } |
| | | }; |
| | | |
| | |
| | | CFliper* pEquipment = new CFliper(); |
| | | pEquipment->setID(EQ_ID_FLIPER); |
| | | pEquipment->setBaseAlarmId(BASE_ALARM_EFEM); |
| | | pEquipment->setName("Fliper"); |
| | | pEquipment->setDescription("Fliper."); |
| | | pEquipment->setName("Fliper(G2)"); |
| | | pEquipment->setDescription("Fliper(G2)."); |
| | | pEquipment->setReadBitBlock(0x4000, 0x45ff); |
| | | pEquipment->setStation(0, 255); |
| | | addToEquipmentList(pEquipment); |
| | |
| | | CVacuumBake* pEquipment = new CVacuumBake(); |
| | | pEquipment->setID(EQ_ID_VACUUMBAKE); |
| | | pEquipment->setBaseAlarmId(BASE_ALARM_EFEM); |
| | | pEquipment->setName("VacuumBake"); |
| | | pEquipment->setDescription("VacuumBake."); |
| | | pEquipment->setName("VacuumBake(G1)"); |
| | | pEquipment->setDescription("VacuumBake(G1)."); |
| | | pEquipment->setReadBitBlock(0x4000, 0x45ff); |
| | | pEquipment->setStation(0, 255); |
| | | addToEquipmentList(pEquipment); |
| | |
| | | return pEquipment; |
| | | } |
| | | |
| | | CMeasurement* CMaster::addMeasurement() |
| | | { |
| | | CMeasurement* pEquipment = new CMeasurement(); |
| | | pEquipment->setID(EQ_ID_MEASUREMENT); |
| | | pEquipment->setBaseAlarmId(BASE_ALARM_EFEM); |
| | | pEquipment->setName("Measurement"); |
| | | pEquipment->setDescription("Measurement."); |
| | | pEquipment->setReadBitBlock(0x6700, 0x6e00); |
| | | pEquipment->setStation(0, 255); |
| | | addToEquipmentList(pEquipment); |
| | | |
| | | pEquipment->init(); |
| | | LOGE("已添加“Measurement”."); |
| | | |
| | | return pEquipment; |
| | | } |
| | | |
| | | void CMaster::onTimer(UINT nTimerid) |
| | | { |
| | | for (auto item : m_listEquipment) { |
| | |
| | | CBonder* pBonder1 = (CBonder*)getEquipment(EQ_ID_Bonder1); |
| | | CBonder* pBonder2 = (CBonder*)getEquipment(EQ_ID_Bonder2); |
| | | CBakeCooling* pBakeCooling = (CBakeCooling*)getEquipment(EQ_ID_BAKE_COOLING); |
| | | CMeasurement* pMeasurement = (CMeasurement*)getEquipment(EQ_ID_MEASUREMENT); |
| | | |
| | | nRet = pLoadPort1->getPin("Out1")->connectPin(pFliper->getPin("In1")); |
| | | if (nRet < 0) { |
| | |
| | | LOGE("连接Bonder2-BakeCooling失败"); |
| | | } |
| | | |
| | | nRet = pBakeCooling->getPin("Out1")->connectPin(pLoadPort3->getPin("In")); |
| | | nRet = pBakeCooling->getPin("Out")->connectPin(pMeasurement->getPin("In")); |
| | | if (nRet < 0) { |
| | | LOGE("连接BakeCooling-LoadPort3失败"); |
| | | } |
| | | |
| | | nRet = pBakeCooling->getPin("Out2")->connectPin(pLoadPort4->getPin("In")); |
| | | nRet = pMeasurement->getPin("Out1")->connectPin(pLoadPort3->getPin("In")); |
| | | if (nRet < 0) { |
| | | LOGE("连接BakeCooling-LoadPort3失败"); |
| | | } |
| | | |
| | | nRet = pMeasurement->getPin("Out2")->connectPin(pLoadPort4->getPin("In")); |
| | | if (nRet < 0) { |
| | | LOGE("连接BakeCooling-LoadPort4失败"); |
| | | } |
| | |
| | | item->serialize(ar); |
| | | } |
| | | } |
| | | |
| | | void CMaster::setState(MASTERSTATE state) |
| | | { |
| | | m_state = state; |
| | | if (m_listener.onMasterStateChanged != nullptr) { |
| | | m_listener.onMasterStateChanged(this, m_state); |
| | | } |
| | | } |
| | | |
| | | static int taskSeqNo = 0; |
| | | CRobotTask* CMaster::createTransferTask(CEquipment* pSrcEq, CEquipment* pTarEq) |
| | | { |
| | | CRobotTask* pTask = nullptr; |
| | | CSlot* pSrcSlot, * pTarSlot; |
| | | pTarSlot = pTarEq->getAvailableSlotForGlass(MaterialsType::G1); |
| | | pSrcSlot = pSrcEq->getNonEmptySlot(MaterialsType::G1); |
| | | if (pSrcSlot == nullptr || nullptr == pTarSlot) { |
| | | pTarSlot = pTarEq->getAvailableSlotForGlass(MaterialsType::G2); |
| | | pSrcSlot = pSrcEq->getNonEmptySlot(MaterialsType::G2); |
| | | } |
| | | |
| | | |
| | | if (pSrcSlot != nullptr && nullptr != pTarSlot) { |
| | | pTask = new CRobotTask(); |
| | | pTask->setContext(pSrcSlot->getContext()); |
| | | pTask->setRobotTransferParam(++taskSeqNo, 1, pSrcSlot->getPosition(), |
| | | pTarSlot->getPosition(), pSrcSlot->getNo(), pTarSlot->getNo()); |
| | | } |
| | | |
| | | |
| | | return pTask; |
| | | } |
| | | } |