| | |
| | | #include "RecipeManager.h" |
| | | #include <fstream> |
| | | #include "SerializeUtil.h" |
| | | #include "CServoUtilsTool.h" |
| | | |
| | | |
| | | namespace SERVO { |
| | | static inline int64_t now_ms_epoch() { |
| | | using namespace std::chrono; |
| | | return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); |
| | | } |
| | | |
| | | CMaster* g_pMaster = NULL; |
| | | |
| | | unsigned __stdcall DispatchThreadFunction(LPVOID lpParam) |
| | |
| | | m_ullRunTime = 0; |
| | | m_state = MASTERSTATE::READY; |
| | | m_pActiveRobotTask = nullptr; |
| | | m_nLastError = 0; |
| | | m_nLastError = ER_CODE_NOERROR; |
| | | m_isCompareMapsBeforeProceeding = FALSE; |
| | | m_bJobMode = FALSE; |
| | | m_bEnableEventReport = true; |
| | |
| | | m_bBatch = false; |
| | | m_nContinuousTransferCount = 0; |
| | | m_nContinuousTransferStep = CTStep_Unknow; |
| | | m_nContinuousWorkingPort = 0; |
| | | m_nContinuousWorkingSlot = 0; |
| | | m_pControlJob = nullptr; |
| | | m_nTestFlag = 0; |
| | | InitializeCriticalSection(&m_criticalSection); |
| | |
| | | BoardVersion version{}; |
| | | int nRet = m_cclink.GetBoardVersion(version); |
| | | if (nRet == 0) { |
| | | LOGI("版本信息:%s.", version.toString().c_str()); |
| | | LOGD("版本信息:%s.", version.toString().c_str()); |
| | | } |
| | | else { |
| | | LOGE("获取CC-Link版本信息失败."); |
| | |
| | | BoardStatus status; |
| | | nRet = m_cclink.GetBoardStatus(status); |
| | | if (nRet == 0) { |
| | | LOGI("状态:%s.", status.toString().c_str()); |
| | | LOGD("状态:%s.", status.toString().c_str()); |
| | | } |
| | | else { |
| | | LOGE("获取CC-Link状态失败."); |
| | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | // 读缓存数据 |
| | | readCache(); |
| | | loadState(); |
| | | |
| | | |
| | | // 定时器 |
| | |
| | | // 监控bit线程 |
| | | m_hReadBitsThreadHandle = (HANDLE)_beginthreadex(NULL, 0, SERVO::ReadBitsThreadFunction, this, |
| | | 0, &m_nReadBitsThreadAddr); |
| | | |
| | | |
| | | // 曲线服务 |
| | | CreateDAQBridgeServer(); |
| | | |
| | | |
| | | LOGI("<Master>初始化完成."); |
| | |
| | | } |
| | | m_listEquipment.clear(); |
| | | |
| | | |
| | | if (m_pCollector != nullptr) { |
| | | m_pCollector->stopLoop(); |
| | | delete m_pCollector; |
| | | m_pCollector = nullptr; |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | |
| | | return 0; |
| | | } |
| | | |
| | | int CMaster::stop() |
| | | int CMaster::stop(int nErCode/* = ER_CODE_NOERROR*/) |
| | | { |
| | | // 运行时间为累加结果,本次停止时刷新; |
| | | lock(); |
| | |
| | | |
| | | |
| | | // 更新状态 |
| | | m_nLastError = nErCode; |
| | | setState(MASTERSTATE::STOPPING); |
| | | |
| | | |
| | |
| | | TRACE("a0001\n", writeCode, retCode); |
| | | }); |
| | | if (nRet != 0) { |
| | | LOGI("<Master>EFEM切换Start状态失败"); |
| | | LOGE("<Master>EFEM切换Start状态失败"); |
| | | m_nLastError = ER_CODE_OPERATION_MODE_FAIL; |
| | | m_strLastError = "EFEM切换Start状态失败."; |
| | | goto WAIT; |
| | | } |
| | |
| | | TRACE("a0002\n"); |
| | | }); |
| | | if (nRet != 0) { |
| | | LOGI("<Master>Bonder1切换Start状态失败"); |
| | | LOGE("<Master>Bonder1切换Start状态失败"); |
| | | m_nLastError = ER_CODE_BONDER_OPERATION_MODE_FAIL; |
| | | m_strLastError = "Bonder1切换Start状态失败."; |
| | | goto WAIT; |
| | | } |
| | |
| | | TRACE("a0003\n"); |
| | | }); |
| | | if (nRet != 0) { |
| | | LOGI("<Master>Bonder2切换Start状态失败"); |
| | | LOGE("<Master>Bonder2切换Start状态失败"); |
| | | m_nLastError = ER_CODE_BONDER_OPERATION_MODE_FAIL; |
| | | m_strLastError = "Bonder2切换Start状态失败."; |
| | | goto WAIT; |
| | | } |
| | |
| | | TRACE("a0004\n"); |
| | | }); |
| | | if (nRet != 0) { |
| | | LOGI("<Master>BakeCooling切换Start状态失败"); |
| | | LOGE("<Master>BakeCooling切换Start状态失败"); |
| | | m_nLastError = ER_CODE_OPERATION_MODE_FAIL; |
| | | m_strLastError = "BakeCooling切换Start状态失败."; |
| | | goto WAIT; |
| | | } |
| | |
| | | TRACE("a0005\n"); |
| | | }); |
| | | if (nRet != 0) { |
| | | LOGI("<Master>VacuumBake切换Start状态失败"); |
| | | LOGE("<Master>VacuumBake切换Start状态失败"); |
| | | m_nLastError = ER_CODE_OPERATION_MODE_FAIL; |
| | | m_strLastError = "VacuumBake切换Start状态失败."; |
| | | goto WAIT; |
| | | } |
| | |
| | | TRACE("a0006\n"); |
| | | }); |
| | | if (nRet != 0) { |
| | | LOGI("<Master>Measurement切换Start状态失败"); |
| | | LOGE("<Master>Measurement切换Start状态失败"); |
| | | m_nLastError = ER_CODE_OPERATION_MODE_FAIL; |
| | | m_strLastError = "Measurement切换Start状态失败."; |
| | | goto WAIT; |
| | | } |
| | |
| | | for (int i = 0; i < 6; i++) { |
| | | if (!bIomcOk[i]) { |
| | | bIomcOk[6] = FALSE; |
| | | LOGI("<Master>%s切换Start状态失败", pEq[i]->getName().c_str()); |
| | | LOGE("<Master>%s切换Start状态失败", pEq[i]->getName().c_str()); |
| | | } |
| | | } |
| | | |
| | |
| | | TRACE("s000%d: ret=%d\n", i + 1, retCode); |
| | | }); |
| | | if (nRet != 0) { |
| | | LOGI("<Master>%s切换Stop状态发送失败", pEq[i]->getName().c_str()); |
| | | LOGE("<Master>%s切换Stop状态发送失败", pEq[i]->getName().c_str()); |
| | | m_nLastError = ER_CODE_OPERATION_MODE_FAIL; |
| | | m_strLastError = pEq[i]->getName() + "切换Stop状态发送失败."; |
| | | bIomcOk[i] = FALSE; |
| | | promises[i].set_value(); // 避免 wait 阻塞 |
| | |
| | | for (int i = 0; i < 6; ++i) { |
| | | if (!bIomcOk[i]) { |
| | | bIomcOk[6] = FALSE; |
| | | LOGI("<Master>%s切换Stop状态失败", pEq[i]->getName().c_str()); |
| | | LOGE("<Master>%s切换Stop状态失败", pEq[i]->getName().c_str()); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | LOGI("<Master>所有设备成功切换到 Stop 模式"); |
| | | setState(MASTERSTATE::READY); |
| | | if(m_nLastError == ER_CODE_NOERROR) |
| | | setState(MASTERSTATE::READY); |
| | | else |
| | | setState(MASTERSTATE::ATHERERROR); |
| | | |
| | | continue; |
| | | } |
| | | |
| | |
| | | |
| | | if (!rmd.armState[0]) { |
| | | // m_nTestFlag = 1; |
| | | if (m_nTestFlag == 1) LOGI("createTransferTask 004df %d, %d", MaterialsType::G1, secondaryType); |
| | | if (m_nTestFlag == 1) LOGD("createTransferTask 004df %d, %d", MaterialsType::G1, secondaryType); |
| | | m_pActiveRobotTask = createTransferTask(pAligner, pVacuumBake, MaterialsType::G1, secondaryType); |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | m_nTestFlag = 0; |
| | |
| | | && pLoadPorts[s]->getPortStatus() == PORT_INUSE) { |
| | | m_pActiveRobotTask = createTransferTask(pLoadPorts[s], pAligner, primaryType, secondaryType, 1, m_bJobMode); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | CGlass* pGlass = (CGlass*)m_pActiveRobotTask->getContext(); |
| | | if (pGlass->getBuddy() != nullptr) { |
| | | delete m_pActiveRobotTask; |
| | | m_pActiveRobotTask = nullptr; |
| | | continue; |
| | | } |
| | | |
| | | pGlass->queue(); |
| | | pGlass->start(); |
| | | pEFEM->setContext(m_pActiveRobotTask->getContext()); |
| | | goto PORT_GET; |
| | | } |
| | |
| | | |
| | | // 批处理模式,最终以此为准,但先保留之前的单片模式 |
| | | else if (m_state == MASTERSTATE::RUNNING_BATCH) { |
| | | // 首选检查有没有CControlJob, 状态等 |
| | | if (m_pControlJob == nullptr) { |
| | | // 1) 控制作业生命周期保障 |
| | | if (m_pControlJob == nullptr) { unlock(); continue; } |
| | | CJState cjst = m_pControlJob->state(); |
| | | if (cjst == CJState::Completed || cjst == CJState::Aborted || cjst == CJState::Failed) { |
| | | unlock(); |
| | | continue; |
| | | } |
| | | CJState state = m_pControlJob->state(); |
| | | if (state == CJState::Completed || state == CJState::Aborted || state == CJState::Failed) { |
| | | // ConrolJpb已完成 |
| | | LOGI("<Master>ControlJob已经完成或失败中断"); |
| | | unlock(); |
| | | continue; |
| | | } |
| | | |
| | | |
| | | if (m_pControlJob->state() == CJState::NoState) { |
| | | if (cjst == CJState::NoState) { |
| | | LOGI("<Master>ControlJob已经进入列队"); |
| | | m_pControlJob->queue(); |
| | | } |
| | | if (m_pControlJob->state() == CJState::Queued) { |
| | | LOGI("<Master>ControlJob已经启动"); |
| | | m_pControlJob->start(); |
| | | |
| | | if (m_listener.onCjStart != nullptr) { |
| | | m_listener.onCjStart(this, m_pControlJob); |
| | | } |
| | | if (m_listener.onCjStart) m_listener.onCjStart(this, m_pControlJob); |
| | | } |
| | | if (m_pControlJob->state() == CJState::Paused) { |
| | | LOGI("<Master>ControlJob已经恢复运行"); |
| | | m_pControlJob->resume(); |
| | | } |
| | | |
| | | |
| | | // 如果当前未选择CProcessJob, 选择一个 |
| | | // 2) 若当前无 PJ,则选择一个并上报 |
| | | if (m_inProcesJobs.empty()) { |
| | | auto pj = acquireNextProcessJob(); |
| | | if (pj != nullptr) { |
| | | if (auto pj = acquireNextProcessJob()) { |
| | | m_inProcesJobs.push_back(pj); |
| | | |
| | | // 这里上报PJ Start事件 |
| | | if (m_listener.onPjStart != nullptr) { |
| | | m_listener.onPjStart(this, pj); |
| | | } |
| | | if (m_listener.onPjStart) m_listener.onPjStart(this, pj); |
| | | } |
| | | } |
| | | if (m_inProcesJobs.empty()) { |
| | | LOGI("<Master>选择当前ProcessJob失败!"); |
| | | LOGE("<Master>选择当前ProcessJob失败!"); |
| | | unlock(); |
| | | continue; |
| | | } |
| | | |
| | | // 如果当前没有Glass, 选择 |
| | | // 3) 若队列无 Glass,拉取到等待队列 |
| | | if (m_queueGlasses.empty()) { |
| | | int nCount = acquireGlassToQueue(); |
| | | LOGI("<Master>已加入 %d 块Glass到工艺列队!", nCount); |
| | | if (nCount > 0) { |
| | | LOGI("<Master>已加入 %d 块Glass到工艺列队!", nCount); |
| | | } |
| | | } |
| | | |
| | | |
| | | // 检测判断robot状态 |
| | | // 4) 机器人状态 |
| | | RMDATA& rmd = pEFEM->getRobotMonitoringData(); |
| | | if (rmd.status != ROBOT_STATUS::Idle && rmd.status != ROBOT_STATUS::Run) { |
| | | unlock(); |
| | | continue; |
| | | unlock(); continue; |
| | | } |
| | | |
| | | // 5) 正在执行的 RobotTask 先让它跑完一拍 |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | if (m_pActiveRobotTask->isPicked()) { |
| | | m_pActiveRobotTask->place(); |
| | | } |
| | | unlock(); |
| | | // 检测到当前有正在下午的任务,确保当前任务完成或中止后继续 |
| | | // LOGI("检测到当前有正在下午的任务,确保当前任务完成或中止后继续..."); |
| | | unlock(); // 等当前任务完成或中止后继续 |
| | | continue; |
| | | } |
| | | |
| | | |
| | | // 此处检测优先类型和次要类型(G1或G2) |
| | | // 如果其中一Bonder有单个玻璃,优先取它的配对类型,否则无所谓了 |
| | | primaryType = MaterialsType::G1; |
| | | secondaryType = MaterialsType::G2; |
| | | if ((!pBonder1->canPlaceGlassInSlot(0) && !pBonder1->canPlaceGlassInSlot(1)) |
| | | && (!pBonder2->canPlaceGlassInSlot(0) && !pBonder2->canPlaceGlassInSlot(1))) { |
| | | // 如果G1和G2都满了,那就看Aligner, 如果Aligner有玻璃为G1, 则取G2 |
| | | CGlass* pGlass = pAligner->getGlassFromSlot(1); |
| | | if (pGlass != nullptr && pGlass->getType() == MaterialsType::G1) { |
| | | primaryType = MaterialsType::G2; |
| | | secondaryType = MaterialsType::G1; |
| | | // 6) ——关键:全局统计 G1/G2 与组数门限(与单片分支对齐)—— |
| | | auto countG1G2 = [&]() { |
| | | int g1 = 0, g2 = 0; |
| | | if (pBonder1->slotHasGlass(0)) g2++; |
| | | if (pBonder1->slotHasGlass(1)) g1++; |
| | | if (pBonder2->slotHasGlass(0)) g2++; |
| | | if (pBonder2->slotHasGlass(1)) g1++; |
| | | if (pFliper->slotHasGlass(0)) g2++; |
| | | if (pVacuumBake->slotHasGlass(0)) g1++; |
| | | if (pVacuumBake->slotHasGlass(1)) g1++; |
| | | if (auto g = pAligner->getGlassFromSlot(0)) { |
| | | auto t = g->getType(); |
| | | if (t == MaterialsType::G1) g1++; else if (t == MaterialsType::G2) g2++; |
| | | } |
| | | } |
| | | else if ((pBonder1->canPlaceGlassInSlot(0) && !pBonder1->canPlaceGlassInSlot(1)) |
| | | || (pBonder2->canPlaceGlassInSlot(0) && !pBonder2->canPlaceGlassInSlot(1))) { |
| | | primaryType = MaterialsType::G2; |
| | | secondaryType = MaterialsType::G1; |
| | | } |
| | | return std::pair<int, int>(g1, g2); |
| | | }; |
| | | |
| | | int g1Count = 0, g2Count = 0; |
| | | std::tie(g1Count, g2Count) = countG1G2(); |
| | | int nGlassGroup = min(g1Count, g2Count); |
| | | int nExtraType = (g1Count == g2Count ? 0 : (g1Count > g2Count ? 1 : 2)); |
| | | |
| | | // Measurement -> LoadPort |
| | | // primary/secondary 统一定义(secondary 默认 G0) |
| | | MaterialsType primaryType = MaterialsType::G1; |
| | | MaterialsType secondaryType = MaterialsType::G0; |
| | | if (nExtraType == 0) primaryType = MaterialsType::G2; // 与单片分支一致 |
| | | else primaryType = MaterialsType::G1; |
| | | |
| | | // 组数门限:≥2 组时不再从 LP 上片,避免堆积(与单片一致) |
| | | bool blockLoadFromLP = (nGlassGroup >= 2); |
| | | |
| | | // 7) Measurement -> LoadPort(固定:G1 优先回 LP) |
| | | if (rmd.armState[0] || rmd.armState[1]) { |
| | | LOGI("Arm1 %s, Arm2 %s.", rmd.armState[0] ? _T("不可用") : _T("可用"), |
| | | LOGD("Arm1 %s, Arm2 %s.", |
| | | rmd.armState[0] ? _T("不可用") : _T("可用"), |
| | | rmd.armState[1] ? _T("不可用") : _T("可用")); |
| | | } |
| | | for (int s = 0; s < 4; s++) { |
| | |
| | | if (!rmd.armState[0] && pLoadPorts[s]->isEnable() |
| | | && (pt == PortType::Unloading || pt == PortType::Both) |
| | | && pLoadPorts[s]->getPortStatus() == PORT_INUSE) { |
| | | m_pActiveRobotTask = createTransferTask(pMeasurement, pLoadPorts[s], primaryType, secondaryType); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | goto BATCH_PORT_PUT; |
| | | } |
| | | m_pActiveRobotTask = createTransferTask(pMeasurement, pLoadPorts[s], MaterialsType::G1, secondaryType); |
| | | if (m_pActiveRobotTask != nullptr) { goto BATCH_PORT_PUT; } |
| | | } |
| | | } |
| | | |
| | | BATCH_PORT_PUT: |
| | | BATCH_PORT_PUT: |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | |
| | | |
| | | // Measurement NG -> LoadPort |
| | | // NG回原位 |
| | | // 8) Measurement NG -> LoadPort(原位回退) |
| | | if (!rmd.armState[1]) { |
| | | m_pActiveRobotTask = createTransferTask_restore(pMeasurement, pLoadPorts); |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | } |
| | | |
| | | |
| | | // BakeCooling ->Measurement |
| | | // 9) BakeCooling -> Measurement |
| | | if (!rmd.armState[0]) { |
| | | m_pActiveRobotTask = createTransferTask_bakecooling_to_measurement(pBakeCooling, pMeasurement); |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | } |
| | | |
| | | |
| | | // BakeCooling内部 |
| | | // Bake -> Cooling |
| | | // 10) BakeCooling 内部(Bake -> Cooling) |
| | | if (!rmd.armState[0]) { |
| | | m_pActiveRobotTask = createTransferTask_bake_to_cooling(pBakeCooling); |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | } |
| | | |
| | | |
| | | // Bonder -> BakeCooling |
| | | // 11) Bonder -> BakeCooling |
| | | if (!rmd.armState[0]) { |
| | | m_pActiveRobotTask = createTransferTask_bonder_to_bakecooling(pBonder1, pBakeCooling); |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | } |
| | | |
| | | if (!rmd.armState[0]) { |
| | | m_pActiveRobotTask = createTransferTask_bonder_to_bakecooling(pBonder2, pBakeCooling); |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | } |
| | | |
| | | // 12) Fliper(G2) -> Bonder(前置:VacuumBake 有 processed G1;输出 G2 到 Bonder slot0) |
| | | if (auto pSrcSlot = pVacuumBake->getProcessedSlot(MaterialsType::G1)) { |
| | | if (!rmd.armState[1] && pBonder1->canPlaceGlassInSlot(0)) { |
| | | m_pActiveRobotTask = createTransferTask(pFliper, pBonder1, MaterialsType::G2, MaterialsType::G0, 2); |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | } |
| | | if (!rmd.armState[1] && pBonder2->canPlaceGlassInSlot(0)) { |
| | | m_pActiveRobotTask = createTransferTask(pFliper, pBonder2, MaterialsType::G2, MaterialsType::G0, 2); |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | } |
| | | } |
| | | |
| | | // Fliper(G2) -> Bonder |
| | | auto pSrcSlot = pVacuumBake->getProcessedSlot(primaryType); |
| | | if (pSrcSlot != nullptr && !rmd.armState[1] && !pBonder1->hasBondGlass()) { |
| | | m_pActiveRobotTask = createTransferTask(pFliper, pBonder1, primaryType, secondaryType, 2); |
| | | // 13) VacuumBake(G1) -> Bonder(槽级判定:slot0(G2) 已有且 slot1(G1) 为空) |
| | | if (!rmd.armState[0] && pBonder1->slotHasGlass(0) && !pBonder1->slotHasGlass(1)) { |
| | | m_pActiveRobotTask = createTransferTask(pVacuumBake, pBonder1, MaterialsType::G1, MaterialsType::G0); |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | } |
| | | if (pSrcSlot != nullptr && !rmd.armState[1] && !pBonder2->hasBondGlass()) { |
| | | m_pActiveRobotTask = createTransferTask(pFliper, pBonder2, primaryType, secondaryType, 2); |
| | | if (!rmd.armState[0] && pBonder2->slotHasGlass(0) && !pBonder2->slotHasGlass(1)) { |
| | | m_pActiveRobotTask = createTransferTask(pVacuumBake, pBonder2, MaterialsType::G1, MaterialsType::G0); |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | } |
| | | |
| | | |
| | | // VacuumBake(G1) -> Bonder |
| | | if (!rmd.armState[0] && !pBonder1->hasBondGlass()) { |
| | | m_pActiveRobotTask = createTransferTask(pVacuumBake, pBonder1, primaryType, secondaryType); |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | } |
| | | |
| | | if (!rmd.armState[0] && !pBonder2->hasBondGlass()) { |
| | | m_pActiveRobotTask = createTransferTask(pVacuumBake, pBonder2, primaryType, secondaryType); |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | } |
| | | |
| | | |
| | | // Aligner -> Fliper(G2) |
| | | // Aligner -> VacuumBake(G1) |
| | | // 14) Aligner -> Fliper(G2) 以及 -> VacuumBake(G1)(固定映射) |
| | | if (!rmd.armState[1]) { |
| | | m_pActiveRobotTask = createTransferTask(pAligner, pFliper, primaryType, secondaryType); |
| | | m_pActiveRobotTask = createTransferTask(pAligner, pFliper, MaterialsType::G2, MaterialsType::G0); |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | } |
| | | |
| | | if (!rmd.armState[0]) { |
| | | m_pActiveRobotTask = createTransferTask(pAligner, pVacuumBake, primaryType, secondaryType); |
| | | m_pActiveRobotTask = createTransferTask(pAligner, pVacuumBake, MaterialsType::G1, MaterialsType::G0); |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | } |
| | | |
| | | |
| | | // Aligner -> LoadPort |
| | | // 15) Aligner -> LoadPort(restore) |
| | | if (!rmd.armState[1]) { |
| | | m_pActiveRobotTask = createTransferTask_restore(pAligner, pLoadPorts); |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | } |
| | | |
| | | // 16) LoadPort -> Aligner(受组数门限控制;统一 buddy/状态时序) |
| | | if (blockLoadFromLP) { unlock(); continue; } |
| | | |
| | | // LoadPort -> Aligner |
| | | for (int s = 0; s < 4; s++) { |
| | | PortType pt = pLoadPorts[s]->getPortType(); |
| | | if (!rmd.armState[0] && pLoadPorts[s]->isEnable() |
| | | && (pt == PortType::Loading || pt == PortType::Both) |
| | | && pLoadPorts[s]->getPortStatus() == PORT_INUSE) { |
| | | m_pActiveRobotTask = createTransferTask(pLoadPorts[s], pAligner, primaryType, secondaryType, m_bJobMode); |
| | | |
| | | m_pActiveRobotTask = createTransferTask(pLoadPorts[s], pAligner, primaryType, secondaryType, 1, m_bJobMode); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | CGlass* pGlass = (CGlass*)m_pActiveRobotTask->getContext(); |
| | | pEFEM->setContext(pGlass); |
| | | auto* pGlass = static_cast<CGlass*>(m_pActiveRobotTask->getContext()); |
| | | if (pGlass->getBuddy() != nullptr) { |
| | | delete m_pActiveRobotTask; m_pActiveRobotTask = nullptr; |
| | | continue; |
| | | } |
| | | |
| | | // 统一:queue -> start -> setContext -> move queue→inProcess -> onPanelStart |
| | | pGlass->queue(); |
| | | pGlass->start(); |
| | | pEFEM->setContext(pGlass); |
| | | |
| | | bool bMoved = glassFromQueueToInPorcess(pGlass); |
| | | if (bMoved) { |
| | | LOGI("<Master>Glass(%s)从等待列队到工艺列队转移成功.", |
| | | pGlass->getID().c_str()); |
| | | LOGI("<Master>Glass(%s)从等待列队到工艺列队转移成功.", pGlass->getID().c_str()); |
| | | } |
| | | else { |
| | | LOGE("<Master>Glass(%s)从等待列队到工艺列队转移失败.", |
| | | pGlass->getID().c_str()); |
| | | LOGE("<Master>Glass(%s)从等待列队到工艺列队转移失败.", pGlass->getID().c_str()); |
| | | } |
| | | |
| | | // 这里上报Panel Start事件 |
| | | if (m_listener.onPanelStart != nullptr) { |
| | | m_listener.onPanelStart(this, pGlass); |
| | | } |
| | | |
| | | if (m_listener.onPanelStart) m_listener.onPanelStart(this, pGlass); |
| | | goto BATCH_PORT_GET; |
| | | } |
| | | } |
| | | } |
| | | |
| | | BATCH_PORT_GET: |
| | | BATCH_PORT_GET: |
| | | CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask); |
| | | |
| | | |
| | | unlock(); |
| | | continue; |
| | | } |
| | | |
| | | |
| | | // 千传模式调度逻辑 |
| | | else if (m_state == MASTERSTATE::RUNNING_CONTINUOUS_TRANSFER) { |
| | |
| | | } |
| | | |
| | | // Measurement -> LoadPort |
| | | for (int s = 0; s < 4; s++) { |
| | | PortType pt = pLoadPorts[s]->getPortType(); |
| | | for (int p = 0; p < 4; p++) { |
| | | if (p != m_nContinuousWorkingPort) continue; |
| | | PortType pt = pLoadPorts[p]->getPortType(); |
| | | if ((m_nContinuousTransferStep == CTStep_Unknow || m_nContinuousTransferStep == CTStep_BakeCooling_Measurement) |
| | | && !rmd.armState[0] && pLoadPorts[s]->isEnable() |
| | | && !rmd.armState[0] && pLoadPorts[p]->isEnable() |
| | | && (pt == PortType::Unloading || pt == PortType::Both) |
| | | && pLoadPorts[s]->getPortStatus() == PORT_INUSE) { |
| | | && pLoadPorts[p]->getPortStatus() == PORT_INUSE) { |
| | | for (int slot = 0; slot < SLOT_MAX; slot++) { |
| | | if (slot != m_nContinuousWorkingSlot) continue; |
| | | m_pActiveRobotTask = createTransferTask_continuous_transfer(pMeasurement, |
| | | 0, pLoadPorts[s], slot); |
| | | 0, pLoadPorts[p], slot); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | m_nContinuousTransferStep = CTStep_Measurement_LoadPort; |
| | | m_nContinuousTransferStep = CTStep_end; |
| | |
| | | } |
| | | |
| | | // LoadPort -> Aligner |
| | | for (int s = 0; s < 4; s++) { |
| | | PortType pt = pLoadPorts[s]->getPortType(); |
| | | for (int p = 0; p < 4; p++) { |
| | | PortType pt = pLoadPorts[p]->getPortType(); |
| | | if ((m_nContinuousTransferStep == CTStep_Unknow || m_nContinuousTransferStep == CTStep_end) |
| | | && !rmd.armState[0] && pLoadPorts[s]->isEnable() |
| | | && !rmd.armState[0] && pLoadPorts[p]->isEnable() |
| | | && (pt == PortType::Loading || pt == PortType::Both) |
| | | && pLoadPorts[s]->getPortStatus() == PORT_INUSE) { |
| | | && pLoadPorts[p]->getPortStatus() == PORT_INUSE) { |
| | | for (int slot = 0; slot < SLOT_MAX; slot++) { |
| | | m_pActiveRobotTask = createTransferTask_continuous_transfer(pLoadPorts[s], |
| | | m_pActiveRobotTask = createTransferTask_continuous_transfer(pLoadPorts[p], |
| | | slot, pAligner, 0); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | m_nContinuousTransferStep = CTStep_LoadPort_Aligner; |
| | | m_nContinuousWorkingPort = p; |
| | | m_nContinuousWorkingSlot = slot; |
| | | LOGI("<ContinuousTransfer>千传测试,开始搬送任务(LoadPort -> Aligner)..."); |
| | | pEFEM->setContext(m_pActiveRobotTask->getContext()); |
| | | goto CT_PORT_GET; |
| | |
| | | BOOL bOk = FALSE; |
| | | lock(); |
| | | if (m_pActiveRobotTask != nullptr) { |
| | | LOGI("<CMaster>onPreFethedOutJob 0001."); |
| | | LOGD("<CMaster>onPreFethedOutJob 0001."); |
| | | if (m_pActiveRobotTask->getSrcPosition() == p->getID()) { |
| | | LOGI("<CMaster>onPreFethedOutJob 0002."); |
| | | LOGD("<CMaster>onPreFethedOutJob 0002."); |
| | | CGlass* pGlass = p->getGlassFromSlot(m_pActiveRobotTask->getSrcSlot()); |
| | | if (pGlass != nullptr) { |
| | | LOGI("<CMaster>onPreFethedOutJob 0003."); |
| | | LOGD("<CMaster>onPreFethedOutJob 0003."); |
| | | CJobDataS* pJobDataS = pGlass->getJobDataS(); |
| | | if (pJobDataS != nullptr |
| | | && pJobDataS->getCassetteSequenceNo() == pJobDataB->getCassetteSequenceNo() |
| | | && pJobDataS->getJobSequenceNo() == pJobDataB->getJobSequenceNo()) { |
| | | bOk = TRUE; |
| | | LOGI("<CMaster>onPreFethedOutJob, 已校验数据一致性."); |
| | | LOGD("<CMaster>onPreFethedOutJob, 已校验数据一致性."); |
| | | } |
| | | LOGI("<CMaster>onPreFethedOutJob 0004."); |
| | | LOGD("<CMaster>onPreFethedOutJob 0004."); |
| | | if (pJobDataS != nullptr) { |
| | | LOGI("<CMaster>onPreFethedOutJob 0005. %d,%d,%d,%d", |
| | | LOGD("<CMaster>onPreFethedOutJob 0005. %d,%d,%d,%d", |
| | | pJobDataS->getCassetteSequenceNo(), |
| | | pJobDataB->getCassetteSequenceNo(), |
| | | pJobDataS->getJobSequenceNo(), |
| | |
| | | if (m_pActiveRobotTask->getSrcPosition() == EQ_ID_MEASUREMENT) { |
| | | CGlass* pGlass = (CGlass*)m_pActiveRobotTask->getContext(); |
| | | pGlass->complete(); |
| | | CGlass* pBuddy = pGlass->getBuddy(); |
| | | if (pBuddy != nullptr) pBuddy->complete(); |
| | | this->saveState(); |
| | | bool bMoved = glassFromInPorcessToComplete(pGlass); |
| | | if (bMoved) { |
| | |
| | | if (m_listener.onCjEnd != nullptr) { |
| | | m_listener.onCjEnd(this, pJob); |
| | | } |
| | | |
| | | completeControlJob(); |
| | | } |
| | | } |
| | | } |
| | |
| | | unlock(); |
| | | } |
| | | }; |
| | | listener.onProcessStateChanged = [&](void* pEquipment, PROCESS_STATE state) -> void { |
| | | listener.onProcessStateChanged = [&](void* pEquipment, int slotNo, PROCESS_STATE state) -> void { |
| | | ASSERT(1 <= slotNo && slotNo <= 8); |
| | | int eqid = ((CEquipment*)pEquipment)->getID(); |
| | | CGlass* pGlass = ((CEquipment*)pEquipment)->getGlassFromSlot(slotNo); |
| | | LOGI("<Master>onProcessStateChanged<%d>", (int)state); |
| | | if (state == PROCESS_STATE::Processing) { |
| | | if (pGlass != nullptr) { |
| | | m_pCollector->batchStart(eqid, |
| | | pGlass->getID().c_str(), 10 * 60 * 1000ULL); |
| | | } |
| | | } |
| | | else if (state == PROCESS_STATE::Complete) { |
| | | m_pCollector->batchStop(eqid); |
| | | } |
| | | }; |
| | | listener.onMapMismatch = [&](void* pEquipment, short scanMap, short downMap) { |
| | | LOGE("<Master-%s>Port InUse, map(%d!=%d)不一致,请检查。", |
| | |
| | | for (auto pj : pjs) { |
| | | auto carrier = pj->getCarrier(pPort->getCassetteId()); |
| | | if (carrier != nullptr) { |
| | | carrier->contexts.clear(); |
| | | for (auto slot : carrier->slots) { |
| | | CGlass* pGlass = pPort->getGlassFromSlot(slot); |
| | | carrier->contexts.push_back((void*)pGlass); |
| | | if (pGlass != nullptr) { |
| | | pGlass->setProcessJob(pj); |
| | | |
| | | PJWarp& jpWarp = pj->getPjWarp(); |
| | | int nRecipeID = RecipeManager::getInstance().getIdByPPID(pj->recipeSpec()); |
| | | RecipeInfo stRecipeInfo = RecipeManager::getInstance().getRecipeByPPID(pj->recipeSpec()); |
| | | std::vector<DeviceRecipe> vecRecipeInfo = stRecipeInfo.vecDeviceList; |
| | | |
| | | pGlass->setScheduledForProcessing(jpWarp.checkSlot[slot-1]); |
| | | pGlass->setType(static_cast<SERVO::MaterialsType>(jpWarp.material[slot-1])); |
| | | |
| | | SERVO::CJobDataS* pJobDataS = pGlass->getJobDataS(); |
| | | if (pJobDataS != nullptr) { |
| | | SERVO::CJobDataS* pJobDataS = pGlass->getJobDataS(); |
| | | pJobDataS->setLotId(pj->getLotId().c_str()); |
| | | pJobDataS->setProductId(pj->getProductId().c_str()); |
| | | pJobDataS->setOperationId(pj->getOperationId().c_str()); |
| | | pJobDataS->setMaterialsType(jpWarp.material[slot - 1]); |
| | | pJobDataS->setMasterRecipe(nRecipeID); |
| | | for (const auto& info : vecRecipeInfo) { |
| | | const std::string& name = info.strDeviceName; |
| | | short nRecipeID = (short)info.nRecipeID; |
| | | |
| | | if (name == EQ_NAME_EFEM) { |
| | | pJobDataS->setDeviceRecipeId(0, nRecipeID); |
| | | } |
| | | else if (name == EQ_NAME_BONDER1) { |
| | | pJobDataS->setDeviceRecipeId(1, nRecipeID); |
| | | } |
| | | else if (name == EQ_NAME_BONDER2) { |
| | | pJobDataS->setDeviceRecipeId(2, nRecipeID); |
| | | } |
| | | else if (name == EQ_NAME_BAKE_COOLING) { |
| | | pJobDataS->setDeviceRecipeId(3, nRecipeID); |
| | | } |
| | | else if (name == EQ_NAME_VACUUMBAKE) { |
| | | pJobDataS->setDeviceRecipeId(4, nRecipeID); |
| | | } |
| | | else if (name == EQ_NAME_MEASUREMENT) { |
| | | pJobDataS->setDeviceRecipeId(5, nRecipeID); |
| | | } |
| | | } |
| | | |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | } |
| | | |
| | | if (m_listener.onLoadPortStatusChanged != nullptr) { |
| | | m_listener.onLoadPortStatusChanged(this, (CEquipment*)pEquipment, status, data); |
| | | } |
| | | }; |
| | | listener.onSVDataReport = [&](void* pEquipment, void* pData) { |
| | | CSVData* pSVData = (CSVData*)pData; |
| | | auto rawData = pSVData->getSVRawData(); |
| | | std::vector<CParam> params; |
| | | ((CEquipment*)pEquipment)->parsingSVData((const char*)rawData.data(), rawData.size(), params); |
| | | |
| | | |
| | | // 以下加入到曲线数据中 |
| | | |
| | | |
| | | const int64_t ts = now_ms_epoch(); |
| | | int eqid = ((CEquipment*)pEquipment)->getID(); |
| | | if (eqid == EQ_ID_Bonder1 || eqid == EQ_ID_Bonder2) { |
| | | // 定义 Bonder 的特定映射 |
| | | std::vector<std::pair<int, int>> bonderMapping = { |
| | | {1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, {7, 7}, |
| | | {8, 8}, {9, 9}, {10, 10}, {11, 11}, {12, 12}, {13, 13}, {14, 14}, {15, 15}, {16, 16} |
| | | }; |
| | | |
| | | CGlass* pGlass = ((CEquipment*)pEquipment)->getGlassFromSlot(0); |
| | | auto& dataTypes = CServoUtilsTool::getEqDataTypes(); |
| | | auto& bonderTypes = dataTypes[eqid]; |
| | | for (const auto& mapping : bonderMapping) { |
| | | int paramIndex = mapping.first; |
| | | int channel = mapping.second; |
| | | |
| | | if (paramIndex < params.size() && channel - 1 < bonderTypes.size()) { |
| | | if(m_pCollector != nullptr) |
| | | m_pCollector->buffersPush(eqid, channel, ts, params.at(paramIndex).getDoubleValue()); |
| | | if(pGlass != nullptr) |
| | | pGlass->addSVData(eqid, bonderTypes[channel], ts, params.at(paramIndex).getDoubleValue()); |
| | | } |
| | | } |
| | | } |
| | | else if (eqid == EQ_ID_VACUUMBAKE) { |
| | | // 定义 VACUUMBAKE 的特定映射 |
| | | std::vector<std::pair<int, int>> vacuumMapping = { |
| | | {1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, {7, 7}, |
| | | {10, 8}, {11, 9}, {12, 10}, {13, 11}, {14, 12}, {15, 13}, {16, 14} |
| | | }; |
| | | |
| | | CGlass* pGlass = ((CEquipment*)pEquipment)->getGlassFromSlot(0); |
| | | auto& dataTypes = CServoUtilsTool::getEqDataTypes(); |
| | | auto& vacuumbakeTypes = dataTypes[eqid]; |
| | | for (const auto& mapping : vacuumMapping) { |
| | | int paramIndex = mapping.first; |
| | | int channel = mapping.second; |
| | | |
| | | if (paramIndex < params.size() && channel - 1 < vacuumbakeTypes.size()) { |
| | | if (m_pCollector != nullptr) |
| | | m_pCollector->buffersPush(eqid, channel, ts, params.at(paramIndex).getDoubleValue()); |
| | | if (pGlass != nullptr) |
| | | pGlass->addSVData(eqid, vacuumbakeTypes[channel], ts, params.at(paramIndex).getDoubleValue()); |
| | | } |
| | | } |
| | | } |
| | | else if (eqid == EQ_ID_BAKE_COOLING) { |
| | | // 定义 BAKE_COOLING 的特定映射 |
| | | std::vector<std::pair<int, int>> coolingMapping = { |
| | | {1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, |
| | | {11, 7}, {12, 8}, {13, 9}, {14, 10}, {15, 11}, {16, 12} |
| | | }; |
| | | |
| | | CGlass* pGlass = ((CEquipment*)pEquipment)->getGlassFromSlot(0); |
| | | auto& dataTypes = CServoUtilsTool::getEqDataTypes(); |
| | | auto& coolingTypes = dataTypes[eqid]; |
| | | for (const auto& mapping : coolingMapping) { |
| | | int paramIndex = mapping.first; |
| | | int channel = mapping.second; |
| | | |
| | | if (paramIndex < params.size() && channel - 1 < coolingTypes.size()) { |
| | | if (m_pCollector != nullptr) |
| | | m_pCollector->buffersPush(eqid, channel, ts, params.at(paramIndex).getDoubleValue()); |
| | | if (pGlass != nullptr) |
| | | pGlass->addSVData(eqid, coolingTypes[channel], ts, params.at(paramIndex).getDoubleValue()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // 以下是输出测试 |
| | | std::string strOut; |
| | | char szBuffer[256]; |
| | | for (auto p : params) { |
| | | if (!strOut.empty()) strOut.append(","); |
| | | if (p.getValueType() == PVT_INT) { |
| | | sprintf_s(szBuffer, 256, "%s:%d", p.getName().c_str(), p.getIntValue()); |
| | | } |
| | | else if (p.getValueType() == PVT_DOUBLE) { |
| | | sprintf_s(szBuffer, 256, "%s:%f", p.getName().c_str(), p.getDoubleValue()); |
| | | } |
| | | strOut.append(szBuffer); |
| | | } |
| | | LOGD("<CMaster-%s>SVDataReport:%s", ((CEquipment*)pEquipment)->getName().c_str(), strOut.c_str()); |
| | | }; |
| | | listener.onPanelDataReport = [&](void* pEquipment, void* pContext) { |
| | | LOGD("<CMaster-%s>onPanelDataReport", ((CEquipment*)pEquipment)->getName().c_str()); |
| | | |
| | | CEquipment* pEq = (CEquipment*)pEquipment; |
| | | CGlass* pGlass = (CGlass*)pContext; |
| | | |
| | | // 如果AOI检测失败,要停机 |
| | | if (pEq->getID() == EQ_ID_MEASUREMENT) { |
| | | LOGD("<CMaster-%s>onPanelDataReport 01", ((CEquipment*)pEquipment)->getName().c_str()); |
| | | if (pGlass->getAOIInspResult() == InspResult::Fail) { |
| | | LOGD("<CMaster-%s>onPanelDataReport 02", ((CEquipment*)pEquipment)->getName().c_str()); |
| | | if (stop() == 0) { |
| | | m_nLastError = ER_CODE_AOI_NG; |
| | | m_strLastError = "AOI检测未通过."; |
| | | } |
| | | } |
| | | } |
| | | |
| | | }; |
| | | pEquipment->setListener(listener); |
| | | pEquipment->setCcLink(&m_cclink); |
| | |
| | | |
| | | |
| | | // 模拟测试 |
| | | /* |
| | | static int aaa = 0; |
| | | aaa++; |
| | | if (aaa % 30 == 0) { |
| | |
| | | if (!m_inProcesGlasses.empty()) { |
| | | CGlass* pGlass = m_inProcesGlasses.front(); |
| | | pGlass->complete(); |
| | | CGlass* pBuddy = pGlass->getBuddy(); |
| | | if (pBuddy != nullptr) pBuddy->complete(); |
| | | glassFromInPorcessToComplete(pGlass); |
| | | this->saveState(); |
| | | |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | */ |
| | | } |
| | | |
| | | void CMaster::connectEquipments() |
| | |
| | | pSrcEq->m_nTestFlag = m_nTestFlag; |
| | | pTarSlot = pTarEq->getAvailableSlotForGlass(primaryType); |
| | | pSrcSlot = pSrcEq->getProcessedSlot(primaryType, bJobMode); |
| | | if (m_nTestFlag == 1) LOGI("createTransferTask 003 %x, %x", pTarSlot, pSrcSlot); |
| | | if (pSrcSlot == nullptr || nullptr == pTarSlot && secondaryType != SERVO::MaterialsType::G0) { |
| | | if (m_nTestFlag == 1) LOGD("createTransferTask 003 %x, %x", pTarSlot, pSrcSlot); |
| | | if ((pSrcSlot == nullptr || nullptr == pTarSlot) && secondaryType != SERVO::MaterialsType::G0) { |
| | | pTarSlot = pTarEq->getAvailableSlotForGlass(secondaryType); |
| | | pSrcSlot = pSrcEq->getProcessedSlot(secondaryType, bJobMode); |
| | | } |
| | | if (m_nTestFlag == 1) LOGI("createTransferTask 004 %x, %x", pTarSlot, pSrcSlot); |
| | | if (m_nTestFlag == 1) LOGD("createTransferTask 004 %x, %x", pTarSlot, pSrcSlot); |
| | | |
| | | if (pSrcSlot != nullptr && nullptr != pTarSlot) { |
| | | pTask = new CRobotTask(); |
| | |
| | | return 0; |
| | | } |
| | | |
| | | void CMaster::setPortType(unsigned int index, BOOL enable, int type, int mode, |
| | | int cassetteType, int transferMode, BOOL autoChangeEnable) |
| | | void CMaster::setPortType(unsigned int index, int type) |
| | | { |
| | | ASSERT(index < 4); |
| | | int eqid[] = { EQ_ID_LOADPORT1, EQ_ID_LOADPORT2, EQ_ID_LOADPORT3, EQ_ID_LOADPORT4}; |
| | | CLoadPort* pPort = (CLoadPort*)getEquipment(eqid[index]); |
| | | pPort->localSetPortType((SERVO::PortType)type); |
| | | } |
| | | |
| | | void CMaster::setPortTypeEx(unsigned int index, BOOL enable, int type, int mode, |
| | | int cassetteType, int transferMode, BOOL autoChangeEnable) |
| | | { |
| | | ASSERT(index < 4); |
| | | int eqid[] = { EQ_ID_LOADPORT1, EQ_ID_LOADPORT2, EQ_ID_LOADPORT3, EQ_ID_LOADPORT4 }; |
| | | CLoadPort* pPort = (CLoadPort*)getEquipment(eqid[index]); |
| | | pPort->localEanblePort(enable); |
| | | pPort->localSetPortType((SERVO::PortType)type); |
| | |
| | | int eqid[] = { EQ_ID_LOADPORT1, EQ_ID_LOADPORT2, EQ_ID_LOADPORT3, EQ_ID_LOADPORT4 }; |
| | | CLoadPort* pPort = (CLoadPort*)getEquipment(eqid[index]); |
| | | pPort->localEanblePort(bEnable); |
| | | } |
| | | |
| | | int CMaster::getPortCassetteSnSeed(int port) |
| | | { |
| | | ASSERT(1 <= port && port <= 4); |
| | | int eqid[] = { EQ_ID_LOADPORT1, EQ_ID_LOADPORT2, EQ_ID_LOADPORT3, EQ_ID_LOADPORT4 }; |
| | | CLoadPort* pPort = (CLoadPort*)getEquipment(eqid[port - 1]); |
| | | return pPort->getPortCassetteSnSeed(); |
| | | } |
| | | |
| | | void CMaster::setPortCassetteSnSeed(int port, int seed) |
| | | { |
| | | ASSERT(1 <= port && port <= 4); |
| | | int eqid[] = { EQ_ID_LOADPORT1, EQ_ID_LOADPORT2, EQ_ID_LOADPORT3, EQ_ID_LOADPORT4 }; |
| | | CLoadPort* pPort = (CLoadPort*)getEquipment(eqid[port - 1]); |
| | | return pPort->setPortCassetteSnSeed(seed); |
| | | } |
| | | |
| | | void CMaster::setCompareMapsBeforeProceeding(BOOL bCompare) |
| | |
| | | temp.push_back(p); |
| | | } |
| | | } |
| | | |
| | | m_processJobs = temp; |
| | | |
| | | |
| | | // 更新context |
| | | std::vector<uint8_t> newSlots; |
| | | std::vector<void*> newContexts; |
| | | for (auto pj : m_processJobs) { |
| | | for (auto& c : pj->carriers()) { |
| | | auto pPort = getPortWithCarrierId(c.carrierId); |
| | | if (pPort == nullptr) continue; |
| | | |
| | | for (auto s : c.slots) { |
| | | auto pGlass = pPort->getGlassFromSlot(s); |
| | | if (pGlass == nullptr) continue; |
| | | |
| | | newSlots.push_back(s); |
| | | newContexts.push_back(pGlass); |
| | | } |
| | | |
| | | pj->setCarrierSlotsAndContexts(c.carrierId, newSlots, newContexts); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | this->saveState(); |
| | | |
| | | return (int)m_processJobs.size(); |
| | |
| | | return true; |
| | | } |
| | | |
| | | bool CMaster::loadState(const std::string& path) |
| | | bool CMaster::loadState() |
| | | { |
| | | // 保存文件路径 |
| | | m_strStatePath = path; |
| | | |
| | | |
| | | std::ifstream ifs(path, std::ios::binary); |
| | | std::ifstream ifs(m_strStatePath, std::ios::binary); |
| | | if (!ifs) return false; |
| | | |
| | | // 文件头 |
| | |
| | | m_pControlJob = new CControlJob(); |
| | | if (!CControlJob::deserialize(ifs, *m_pControlJob)) return false; |
| | | } |
| | | |
| | | else { |
| | | return false; |
| | | } |
| | | |
| | | // 读取 ProcessJob 列表 |
| | | uint32_t count = 0; |
| | |
| | | m_pControlJob->setPJs(tempPjs); |
| | | |
| | | |
| | | // 更新contexts |
| | | auto pjs = m_pControlJob->getPjs(); |
| | | for (auto pj : pjs) { |
| | | for (auto& c : pj->carriers()) { |
| | | auto p = getPortWithCarrierId(c.carrierId); |
| | | if (p == nullptr) continue; |
| | | |
| | | std::vector<void*> contexts; |
| | | for (auto s : c.slots) { |
| | | auto g = getGlass(p->getIndex(), s - 1); |
| | | if (g == nullptr) continue; |
| | | contexts.push_back(g); |
| | | } |
| | | pj->setCarrierContexts(c.carrierId, contexts); |
| | | } |
| | | } |
| | | |
| | | |
| | | // 如果版本升级,可在这里判断 version 来加载新字段 |
| | | |
| | | |
| | | return true; |
| | | } |
| | | |
| | | void CMaster::setStateFile(const std::string& path) |
| | | { |
| | | m_strStatePath = path; |
| | | } |
| | | |
| | | CProcessJob* CMaster::acquireNextProcessJob() |
| | |
| | | int nCount = 0; |
| | | for (auto* pj : m_inProcesJobs) { |
| | | // 遍历 PJ 的 carriers 和 slots |
| | | if (pj->carriers().empty()) continue; |
| | | for (auto& cs : pj->carriers()) { |
| | | for (auto ctx : cs.contexts) { |
| | | CGlass* pGlass = (CGlass*)ctx; |
| | |
| | | |
| | | return nullptr; |
| | | } |
| | | |
| | | |
| | | bool CMaster::completeControlJob() |
| | | { |
| | | if (m_pControlJob == nullptr) { |
| | | return false; |
| | | } |
| | | for (auto item : m_processJobs) { |
| | | if (item->state() != PJState::Completed) return false; |
| | | } |
| | | if (m_pControlJob->state() != CJState::Completed) |
| | | return false; |
| | | |
| | | |
| | | |
| | | // 释放Job相关 |
| | | for (auto item : m_processJobs) { |
| | | delete item; |
| | | } |
| | | m_processJobs.clear(); |
| | | if (m_pControlJob != nullptr) { |
| | | delete m_pControlJob; |
| | | m_pControlJob = nullptr; |
| | | } |
| | | |
| | | // 注意要释放引用 |
| | | m_inProcesJobs.clear(); |
| | | m_completeProcessJobs.clear(); |
| | | m_queueGlasses.clear(); |
| | | m_inProcesGlasses.clear(); |
| | | m_completeGlasses.clear(); |
| | | |
| | | |
| | | saveState(); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | bool CMaster::forceCompleteControlJob(std::string description) |
| | | { |
| | | if (m_pControlJob == nullptr || m_state != SERVO::MASTERSTATE::READY) { |
| | | return false; |
| | | } |
| | | for (auto item : m_processJobs) { |
| | | item->abort(description); |
| | | } |
| | | m_pControlJob->abort(description); |
| | | |
| | | |
| | | // 释放Job相关 |
| | | for (auto item : m_processJobs) { |
| | | delete item; |
| | | } |
| | | m_processJobs.clear(); |
| | | if (m_pControlJob != nullptr) { |
| | | delete m_pControlJob; |
| | | m_pControlJob = nullptr; |
| | | } |
| | | |
| | | // 注意要释放引用 |
| | | m_inProcesJobs.clear(); |
| | | m_completeProcessJobs.clear(); |
| | | m_queueGlasses.clear(); |
| | | m_inProcesGlasses.clear(); |
| | | m_completeGlasses.clear(); |
| | | |
| | | |
| | | saveState(); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | bool CMaster::canCreateControlJob() |
| | | { |
| | | return m_pControlJob == nullptr; |
| | | } |
| | | |
| | | bool CMaster::canCompleteControlJob() |
| | | { |
| | | return m_pControlJob != nullptr && m_state == SERVO::MASTERSTATE::READY; |
| | | } |
| | | |
| | | bool CMaster::canDeleteControlJob() |
| | | { |
| | | return m_pControlJob != nullptr |
| | | && m_pControlJob->state() == CJState::NoState |
| | | && m_state == SERVO::MASTERSTATE::READY; |
| | | } |
| | | |
| | | int CMaster::getWipGlasses(std::vector<CGlass*>& glasses) |
| | | { |
| | | for (auto eq : m_listEquipment) { |
| | | auto p = dynamic_cast<CLoadPort*>(eq); |
| | | if (p == nullptr) { |
| | | eq->getAllGlass(glasses); |
| | | } |
| | | |
| | | } |
| | | |
| | | return (int)glasses.size(); |
| | | } |
| | | |
| | | int CMaster::getLastError() |
| | | { |
| | | return m_nLastError; |
| | | } |
| | | |
| | | std::string& CMaster::getLastErrorText() |
| | | { |
| | | return m_strLastError; |
| | | } |
| | | |
| | | void CMaster::test() |
| | | { |
| | | if (stop() == 0) { |
| | | m_nLastError = ER_CODE_AOI_NG; |
| | | m_strLastError = "AOI检测未通过."; |
| | | } |
| | | } |
| | | |
| | | bool CMaster::moveGlassToBuf(int eqid, int slotNo) |
| | | { |
| | | CEquipment* pEquipment = getEquipment(eqid); |
| | | if (pEquipment == nullptr) return false; |
| | | |
| | | CSlot* pSlot = pEquipment->getSlotWithNo(slotNo); |
| | | if (pSlot == nullptr) return false; |
| | | |
| | | CGlass* pGlass = (CGlass*)pSlot->getContext(); |
| | | m_bufGlass.push_back(pGlass); |
| | | pGlass->addRef(); |
| | | pSlot->setContext(nullptr); |
| | | |
| | | m_bDataModify = TRUE; |
| | | if (m_listener.onEqDataChanged != nullptr) { |
| | | m_listener.onEqDataChanged(this, pEquipment, 0); |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | bool CMaster::moveGlassToSlot(int eqid, int slotNo) |
| | | { |
| | | CEquipment* pEquipment = getEquipment(eqid); |
| | | if (pEquipment == nullptr) return false; |
| | | |
| | | CSlot* pSlot = pEquipment->getSlotWithNo(slotNo); |
| | | if (pSlot == nullptr) return false; |
| | | if (m_bufGlass.empty()) return false; |
| | | |
| | | CGlass* pGlass = m_bufGlass.front(); |
| | | m_bufGlass.pop_front(); |
| | | if (pGlass == nullptr) return false; |
| | | pSlot->setContext(pGlass); |
| | | pGlass->release(); |
| | | |
| | | m_bDataModify = TRUE; |
| | | if (m_listener.onEqDataChanged != nullptr) { |
| | | m_listener.onEqDataChanged(this, pEquipment, 0); |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | CGlass* CMaster::getGlass(int scrPort, int scrSlot) |
| | | { |
| | | for (auto eq : m_listEquipment) { |
| | | std::vector<CGlass*> glasses; |
| | | eq->getAllGlass(glasses); |
| | | for (auto g : glasses) { |
| | | int p, s; |
| | | g->getOrginPort(p, s); |
| | | if (p == scrPort && s == scrSlot) { |
| | | return g; |
| | | } |
| | | } |
| | | } |
| | | |
| | | return nullptr; |
| | | } |
| | | |
| | | void CMaster::CreateDAQBridgeServer() |
| | | { |
| | | auto connectionStatusCallback = [&](int code, const std::string& status) { |
| | | LOGI("<DAQBridge>status:", status.c_str()); |
| | | }; |
| | | auto rawDataCallback = [](const std::vector<uint8_t>& bytes) { |
| | | |
| | | }; |
| | | |
| | | // 事件:有人连入/断开就上日志 |
| | | auto clieintEventCallback = [](const std::string& ip, uint16_t port, bool connected) { |
| | | LOGI("<DAQBridge>[Client %s] %s:%u", connected ? _T("JOIN") : _T("LEAVE"), ip.c_str(), port); |
| | | }; |
| | | |
| | | if (m_pCollector == nullptr) { |
| | | m_pCollector = new Collector(); |
| | | m_pCollector->setConnectionStatusCallback(connectionStatusCallback); |
| | | m_pCollector->setRawDataCallback(rawDataCallback); |
| | | m_pCollector->setClientEventCallback(clieintEventCallback); |
| | | m_pCollector->createServer(8081); |
| | | m_pCollector->startLoop(10); |
| | | |
| | | // 1) 注册机台(推荐:先注册 id + 机器名称) |
| | | RetentionPolicy defP; defP.mode = RetainMode::ByCount; defP.maxSamples = 200; |
| | | m_pCollector->registryAddMachine(EQ_ID_Bonder1, "Bonder1", defP); |
| | | m_pCollector->registryAddMachine(EQ_ID_Bonder2, "Bonder2", defP); |
| | | m_pCollector->registryAddMachine(EQ_ID_VACUUMBAKE, "前烘烤", defP); |
| | | m_pCollector->registryAddMachine(EQ_ID_BAKE_COOLING, "烘烤冷却", defP); |
| | | |
| | | |
| | | // 2) 为通道设置“曲线名称” |
| | | auto& dataTypes = CServoUtilsTool::getEqDataTypes(); |
| | | auto& bonderTypes = dataTypes[EQ_ID_Bonder1]; |
| | | for (size_t i = 0; i < bonderTypes.size(); ++i) { |
| | | m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, i + 1, bonderTypes[i].c_str()); |
| | | m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, i + 1, bonderTypes[i].c_str()); |
| | | } |
| | | |
| | | auto& vacuumbakeTypes = dataTypes[EQ_ID_VACUUMBAKE]; |
| | | for (size_t i = 0; i < vacuumbakeTypes.size(); ++i) { |
| | | m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, i + 1, vacuumbakeTypes[i].c_str()); |
| | | } |
| | | |
| | | auto& coolingTypes = dataTypes[EQ_ID_BAKE_COOLING]; |
| | | for (size_t i = 0; i < coolingTypes.size(); ++i) { |
| | | m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, i + 1, coolingTypes[i].c_str()); |
| | | } |
| | | } |
| | | } |
| | | } |