chenluhua1980
3 天以前 351086486441b80cfe71550b43cbe1e4dc440f5d
SourceCode/Bond/Servo/CMaster.cpp
@@ -63,6 +63,7 @@
      m_ullRunTime = 0;
      m_state = MASTERSTATE::READY;
      m_curveMode = CurveMode::Production;
      m_schedulingMode = SchedulingMode::Production;
      m_pActiveRobotTask = nullptr;
      m_nLastError = ER_CODE_NOERROR;
      m_isCompareMapsBeforeProceeding = FALSE;
@@ -118,6 +119,7 @@
         ::CloseHandle(m_hEventDispatchThreadExit[1]);
         m_hEventDispatchThreadExit[1] = nullptr;
      }
      DeleteCriticalSection(&m_criticalSection);
   }
@@ -295,11 +297,11 @@
         };
         for (uint32_t mid : mids) {
            if (mode == CurveMode::EmptyChamber) {
               m_pCollector->buffersStart(mid); // 空腔模式:允许无批次直接采样
               m_pCollector->batchStart(mid, "EMPTY_CHAMBER", 30 * 60 * 1000ULL); // 空腔模式:启动采样批次
            }
            else {
               m_pCollector->buffersStop(mid);
               m_pCollector->buffersClear(mid);
               m_pCollector->batchStop(mid);
               m_pCollector->buffersClear(mid); // 切回生产模式,清掉空腔数据
            }
         }
      }
@@ -309,6 +311,17 @@
   CurveMode CMaster::getCurveMode() const
   {
      return m_curveMode;
   }
   void CMaster::setSchedulingMode(SchedulingMode mode)
   {
      m_schedulingMode = mode;
      LOGI("<Master>SchedulingMode=%s", mode == SchedulingMode::Production ? "Production" : "Tuning");
   }
   SchedulingMode CMaster::getSchedulingMode() const
   {
      return m_schedulingMode;
   }
   int CMaster::term()
@@ -336,6 +349,13 @@
      }
      m_listEquipment.clear();
      // release manual-remove buffer before glass pool is torn down
      for (auto* pGlass : m_bufGlass) {
         if (pGlass != nullptr) {
            pGlass->release();
         }
      }
      m_bufGlass.clear();
      if (m_pCollector != nullptr) {
         m_pCollector->stopLoop();
@@ -738,14 +758,22 @@
            // Measurement -> LoadPort
            for (int s = 0; s < 4; s++) {
               PortType pt = pLoadPorts[s]->getPortType();
               if (!rmd.armState[0] && pLoadPorts[s]->isEnable()
                  && (pt == PortType::Unloading || pt == PortType::Both)
                  && pLoadPorts[s]->getPortStatus() == PORT_INUSE) {
                  m_pActiveRobotTask = createTransferTask(pMeasurement, pLoadPorts[s], MaterialsType::G1, secondaryType);
                  if (m_pActiveRobotTask != nullptr) {
                     goto PORT_PUT;
            if (m_schedulingMode == SchedulingMode::Production) {
               if (!rmd.armState[0]) {
                  m_pActiveRobotTask = createTransferTask_returnOrigin(pMeasurement, pLoadPorts);
                  CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
               }
            }
            else {
               for (int s = 0; s < 4; s++) {
                  PortType pt = pLoadPorts[s]->getPortType();
                  if (!rmd.armState[0] && pLoadPorts[s]->isEnable()
                     && (pt == PortType::Unloading || pt == PortType::Both)
                     && pLoadPorts[s]->getPortStatus() == PORT_INUSE) {
                     m_pActiveRobotTask = createTransferTask(pMeasurement, pLoadPorts[s], MaterialsType::G1, secondaryType);
                     if (m_pActiveRobotTask != nullptr) {
                        goto PORT_PUT;
                     }
                  }
               }
            }
@@ -1000,19 +1028,27 @@
            // 组数门限:≥2 组时不再从 LP 上片,避免堆积(与单片一致)
            bool blockLoadFromLP = (nGlassGroup >= 2);
            // 7) Measurement -> LoadPort(固定:G1 优先回 LP)
            // 7) Measurement -> LoadPort
            if (rmd.armState[0] || rmd.armState[1]) {
               LOGD("Arm1 %s, Arm2 %s.",
                  rmd.armState[0] ? _T("不可用") : _T("可用"),
                  rmd.armState[1] ? _T("不可用") : _T("可用"));
            }
            for (int s = 0; s < 4; s++) {
               PortType pt = pLoadPorts[s]->getPortType();
               if (!rmd.armState[0] && pLoadPorts[s]->isEnable()
                  && (pt == PortType::Unloading || pt == PortType::Both)
                  && pLoadPorts[s]->getPortStatus() == PORT_INUSE) {
                  m_pActiveRobotTask = createTransferTask(pMeasurement, pLoadPorts[s], MaterialsType::G1, secondaryType);
                  if (m_pActiveRobotTask != nullptr) { goto BATCH_PORT_PUT; }
            if (m_schedulingMode == SchedulingMode::Production) {
               if (!rmd.armState[0]) {
                  m_pActiveRobotTask = createTransferTask_returnOrigin(pMeasurement, pLoadPorts);
                  CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
               }
            }
            else {
               for (int s = 0; s < 4; s++) {
                  PortType pt = pLoadPorts[s]->getPortType();
                  if (!rmd.armState[0] && pLoadPorts[s]->isEnable()
                     && (pt == PortType::Unloading || pt == PortType::Both)
                     && pLoadPorts[s]->getPortStatus() == PORT_INUSE) {
                     m_pActiveRobotTask = createTransferTask(pMeasurement, pLoadPorts[s], MaterialsType::G1, secondaryType);
                     if (m_pActiveRobotTask != nullptr) { goto BATCH_PORT_PUT; }
                  }
               }
            }
@@ -1785,18 +1821,23 @@
               int paramIndex = mapping.first;
               int channel = mapping.second;
               if (paramIndex < params.size() && channel - 1 < vacuumbakeTypes.size()) {
               if (paramIndex < params.size()) {
                  auto& param = params.at(paramIndex);
                  double value = param.getDoubleValue();
                  const std::string& dataType = vacuumbakeTypes[channel - 1];
                  const std::string& paramName = param.getName();
                  const char slotTag = !paramName.empty() ? paramName[0] : '\0';
                  const int typeIndex = (slotTag == 'B') ? (channel - 8) : (channel - 1);
                  if (typeIndex < 0 || typeIndex >= (int)vacuumbakeTypes.size()) {
                     continue;
                  }
                  const int pushChannel = typeIndex + 1;
                  const std::string& dataType = vacuumbakeTypes[typeIndex];
                  if (m_pCollector != nullptr) {
                     if (slotTag == 'A')
                        m_pCollector->buffersPush(SlotToMid(eqid, 1), channel, ts, value);
                        m_pCollector->buffersPush(SlotToMid(eqid, 1), pushChannel, ts, value);
                     else if (slotTag == 'B')
                        m_pCollector->buffersPush(SlotToMid(eqid, 2), channel, ts, value);
                        m_pCollector->buffersPush(SlotToMid(eqid, 2), pushChannel, ts, value);
                  }
                  // 根据腔体前缀写入对应 Slot 的玻璃
@@ -1830,20 +1871,25 @@
               int paramIndex = mapping.first;
               int channel = mapping.second;
               if (paramIndex < params.size() && channel - 1 < coolingTypes.size()) {
               if (paramIndex < params.size()) {
                  auto& param = params.at(paramIndex);
                  double value = param.getDoubleValue();
                  const std::string& dataType = coolingTypes[channel - 1];
                  const std::string& paramName = param.getName();
                  const char slotTag = !paramName.empty() ? paramName[0] : '\0';
                  const bool paramIsBake = paramName.find("烘烤") != std::string::npos;
                  const bool paramIsCooling = paramName.find("冷却") != std::string::npos;
                  const int typeIndex = (slotTag == 'B') ? (channel - 7) : (channel - 1);
                  if (typeIndex < 0 || typeIndex >= (int)coolingTypes.size()) {
                     continue;
                  }
                  const int pushChannel = typeIndex + 1;
                  const std::string& dataType = coolingTypes[typeIndex];
                  if (m_pCollector != nullptr && paramIsBake) {
                     if (slotTag == 'A')
                        m_pCollector->buffersPush(SlotToMid(eqid, 1), channel, ts, value);
                        m_pCollector->buffersPush(SlotToMid(eqid, 1), pushChannel, ts, value);
                     else if (slotTag == 'B')
                        m_pCollector->buffersPush(SlotToMid(eqid, 3), channel, ts, value);
                        m_pCollector->buffersPush(SlotToMid(eqid, 3), pushChannel, ts, value);
                  }
                  if (!dataType.empty()) {
@@ -1910,6 +1956,16 @@
      listener.onReceivedJob = [&](void* pEquipment, int port, CJobDataS* pJobDataS) {
         if (m_listener.onJobReceived != nullptr) {
            m_listener.onJobReceived(this, (CEquipment*)pEquipment, port, pJobDataS);
         }
      };
      listener.onSentOutJob = [&](void* pEquipment, int port, CJobDataS* pJobDataS) {
         if (m_listener.onJobSentOut != nullptr) {
            m_listener.onJobSentOut(this, (CEquipment*)pEquipment, port, pJobDataS);
         }
      };
      listener.onEqStatusChanged = [&](void* pEquipment, int unitId, int status, int reason) {
         if (m_listener.onEqStatusChanged != nullptr) {
            m_listener.onEqStatusChanged(this, (CEquipment*)pEquipment, unitId, status, reason);
         }
      };
      pEquipment->setListener(listener);
@@ -2421,11 +2477,19 @@
      nRet = pLoadPort1->getPin("Out")->connectPin(pAligner->getPin("In1"));
      if (nRet < 0) {
         LOGE("连接LoadPort1-Fliper失败");
         LOGE("连接LoadPort1-Aligner失败");
      }
      nRet = pLoadPort2->getPin("Out")->connectPin(pAligner->getPin("In2"));
      if (nRet < 0) {
         LOGE("连接LoadPort1-Fliper失败");
         LOGE("连接LoadPort2-Aligner失败");
      }
      nRet = pLoadPort3->getPin("Out")->connectPin(pAligner->getPin("In3"));
      if (nRet < 0) {
         LOGE("连接LoadPort3-Aligner失败");
      }
      nRet = pLoadPort4->getPin("Out")->connectPin(pAligner->getPin("In4"));
      if (nRet < 0) {
         LOGE("连接LoadPort4-Aligner失败");
      }
      nRet = pAligner->getPin("Out1")->connectPin(pFliper->getPin("In"));
@@ -2470,14 +2534,29 @@
         LOGE("连接BakeCooling-LoadPort3失败");
      }
      nRet = pMeasurement->getPin("Out1")->connectPin(pLoadPort3->getPin("In"));
      if (nRet < 0) {
         LOGE("连接BakeCooling-LoadPort3失败");
      }
      if (m_schedulingMode == SchedulingMode::Production) {
         // 生产模式:测量输出回到 G1 原位(默认 Port1 / Port3)
         nRet = pMeasurement->getPin("Out1")->connectPin(pLoadPort1->getPin("In"));
         if (nRet < 0) {
            LOGE("连接Measurement-LoadPort1失败");
         }
      nRet = pMeasurement->getPin("Out2")->connectPin(pLoadPort4->getPin("In"));
      if (nRet < 0) {
         LOGE("连接BakeCooling-LoadPort4失败");
         nRet = pMeasurement->getPin("Out2")->connectPin(pLoadPort3->getPin("In"));
         if (nRet < 0) {
            LOGE("连接Measurement-LoadPort3失败");
         }
      }
      else {
         // 调机模式:维持原连接(Out1->Port3, Out2->Port4)
         nRet = pMeasurement->getPin("Out1")->connectPin(pLoadPort3->getPin("In"));
         if (nRet < 0) {
            LOGE("连接Measurement-LoadPort3失败");
         }
         nRet = pMeasurement->getPin("Out2")->connectPin(pLoadPort4->getPin("In"));
         if (nRet < 0) {
            LOGE("连接Measurement-LoadPort4失败");
         }
      }
   }
@@ -2707,6 +2786,57 @@
      return pTask;
   }
   CRobotTask* CMaster::createTransferTask_returnOrigin(CEquipment* pEqSrc, CLoadPort** pPorts)
   {
      if (!pEqSrc->IsEnabled()) {
         return nullptr;
      }
      CSlot* pSrcSlot = pEqSrc->getProcessedSlot(MaterialsType::G1, m_bJobMode);
      if (pSrcSlot == nullptr) {
         return nullptr;
      }
      CGlass* pGlass = (CGlass*)pSrcSlot->getContext();
      if (pGlass == nullptr) {
         return nullptr;
      }
      int port = 0, slot = 0;
      pGlass->getOrginPort(port, slot);
      if (port < 0 || port >= 4 || slot < 0 || slot >= SLOT_MAX) {
         return nullptr;
      }
      CLoadPort* pPort = pPorts[port];
      if (pPort == nullptr || !pPort->isEnable()) {
         return nullptr;
      }
      PortType pt = pPort->getPortType();
      if (!(pt == PortType::Unloading || pt == PortType::Both)) {
         return nullptr;
      }
      if (pPort->getPortStatus() != PORT_INUSE) {
         return nullptr;
      }
      CSlot* pTarSlot = pPort->getSlot(slot);
      if (pTarSlot == nullptr) {
         return nullptr;
      }
      if (!pTarSlot->isEnable() || pTarSlot->isLock() || pTarSlot->getContext() != nullptr) {
         return nullptr;
      }
      CRobotTask* pTask = new CRobotTask();
      pTask->setContext(pSrcSlot->getContext());
      pTask->setEFEM((CEFEM*)getEquipment(EQ_ID_EFEM));
      taskSeqNo = pTask->setRobotTransferParam(taskSeqNo, 1, pSrcSlot->getPosition(),
         pTarSlot->getPosition(), pSrcSlot->getNo(), pTarSlot->getNo());
      return pTask;
   }
   CRobotTask* CMaster::createTransferTask_continuous_transfer(CEquipment* pSrcEq, int nSrcSlot,
      CEquipment* pTarEq, int nTarSlot, int armNo/* = 1*/)
   {
@@ -2816,6 +2946,19 @@
      pPort->localSetCessetteType(type);
   }
   void CMaster::applySchedulingModePortMapping()
   {
      // 生产模式:固定 Port1/Port3 为 G1,Port2/Port4 为 G2(G4 未定义,按 G2 处理)
      if (m_schedulingMode != SchedulingMode::Production) {
         return;
      }
      setPortCassetteType(0, SERVO::CassetteType::G1);
      setPortCassetteType(1, SERVO::CassetteType::G2);
      setPortCassetteType(2, SERVO::CassetteType::G1);
      setPortCassetteType(3, SERVO::CassetteType::G2);
   }
   void CMaster::setPortEnable(unsigned int index, BOOL bEnable)
   {
      ASSERT(index < 4);
@@ -2879,7 +3022,54 @@
      static int pid[] = { EQ_ID_LOADPORT1, EQ_ID_LOADPORT2, EQ_ID_LOADPORT3, EQ_ID_LOADPORT4};
      CLoadPort* pPort = (CLoadPort*)getEquipment(pid[port]);
      pPort->sendCassetteCtrlCmd(CCC_PROCESS_START, nullptr, 0, 0, 0, nullptr, nullptr);
      if (pPort == nullptr) return -1;
      short jobExistence[12] = { 0 };
      short slotProcess = 0;
      short jobCount = 0; // 0 = Process All Glass
      bool anyScheduled = false;
      // Prefer hardware scan map for job existence (first 16 slots).
      const short scanMap = pPort->getScanCassetteMap();
      if (scanMap != 0) {
         jobExistence[0] = scanMap;
      }
      const int maxSlots = 12 * 16;
      const int totalSlots = (SLOT_MAX < maxSlots) ? SLOT_MAX : maxSlots;
      for (int slot = 1; slot <= totalSlots; ++slot) {
         CGlass* pGlass = pPort->getGlassFromSlot(slot);
         if (pGlass == nullptr) continue;
         const int wordIndex = (slot - 1) / 16;
         const int bitIndex = (slot - 1) % 16;
         jobExistence[wordIndex] = (short)(jobExistence[wordIndex] | (1 << bitIndex));
         if (slot <= 16 && pGlass->isScheduledForProcessing()) {
            slotProcess = (short)(slotProcess | (1 << bitIndex));
            anyScheduled = true;
         }
      }
      if (!anyScheduled) {
         slotProcess = jobExistence[0];
      }
      bool hasExistence = false;
      for (short w : jobExistence) {
         if (w != 0) { hasExistence = true; break; }
      }
      const int portStatus = pPort->getPortStatus();
      if (!hasExistence) {
         LOGE("ProcessStart blocked (ProceedWithCarrier): no JobExistence map (port=%u, portStatus=%d, scanMap=%d, cassetteId=%s).",
            port + 1, portStatus, scanMap, pPort->getCassetteId().c_str());
         return -2;
      }
      if (portStatus != PORT_INUSE) {
         LOGW("ProcessStart warning (ProceedWithCarrier): port status is %d (expected INUSE).", portStatus);
      }
      pPort->sendCassetteCtrlCmd(CCC_PROCESS_START, jobExistence, 12, slotProcess, jobCount, nullptr, nullptr);
      return 0;
   }
@@ -3467,6 +3657,11 @@
      }
      m_pControlJob->abort(description);
      // 先上报一次状态变化(便于 PrJobAbort 触发)
      if (m_listener.onControlJobChanged) {
         notifyControlJobChanged();
      }
      // 释放Job相关
      for (auto item : m_processJobs) {
@@ -3551,6 +3746,15 @@
      if (pSlot == nullptr) return false;
      CGlass* pGlass = (CGlass*)pSlot->getContext();
      if (pGlass == nullptr) return false;
      // Buffer 上限为 1:新搬出时丢弃旧的
      if (!m_bufGlass.empty()) {
         for (auto* oldGlass : m_bufGlass) {
            if (oldGlass != nullptr) oldGlass->release();
         }
         m_bufGlass.clear();
      }
      m_bufGlass.push_back(pGlass);
      pGlass->addRef();
      pSlot->setContext(nullptr);
@@ -3662,7 +3866,7 @@
               MID_BakeCoolingA, MID_BakeCoolingB
            };
            for (uint32_t mid : mids) {
               m_pCollector->buffersStart(mid);
               m_pCollector->batchStart(mid, "EMPTY_CHAMBER", 10 * 60 * 1000ULL);
            }
         }
      }