| | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | // 读缓存数据 |
| | | readCache(); |
| | | loadState(); |
| | | |
| | | |
| | | // 定时器 |
| | |
| | | |
| | | // 批处理模式,最终以此为准,但先保留之前的单片模式 |
| | | 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已完成 |
| | | LOGE("<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()) { |
| | |
| | | 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]) { |
| | | LOGD("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(); |
| | | auto* pGlass = static_cast<CGlass*>(m_pActiveRobotTask->getContext()); |
| | | if (pGlass->getBuddy() != nullptr) { |
| | | delete m_pActiveRobotTask; |
| | | m_pActiveRobotTask = nullptr; |
| | | delete m_pActiveRobotTask; m_pActiveRobotTask = nullptr; |
| | | continue; |
| | | } |
| | | |
| | | pEFEM->setContext(pGlass); |
| | | // 统一: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) { |
| | |
| | | |
| | | |
| | | // 模拟测试 |
| | | /* |
| | | static int aaa = 0; |
| | | aaa++; |
| | | if (aaa % 30 == 0) { |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | */ |
| | | } |
| | | |
| | | void CMaster::connectEquipments() |
| | |
| | | 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->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 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; |
| | | } |
| | | } |