chenluhua1980
2 天以前 b099ab8b7c83dc957bd9777a0bb90c1d8202056b
SourceCode/Bond/Servo/CMaster.cpp
@@ -62,6 +62,8 @@
      m_ullStartTime = 0;
      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 +120,7 @@
         m_hEventDispatchThreadExit[1] = nullptr;
      }
      DeleteCriticalSection(&m_criticalSection);
   }
@@ -138,11 +141,18 @@
   int CMaster::init()
   {
      const ULONGLONG boot_master_begin = GetTickCount64();
      LOGI("<Master>正在初始化...");
      LOGI("[BOOT][MASTER] init begin");
      //    cclink
      if (m_cclink.Connect(CC_LINK_IE_CONTROL_CHANNEL(1)) != 0) {
      const ULONGLONG boot_cclink_begin = GetTickCount64();
      const int cc_ret = m_cclink.Connect(CC_LINK_IE_CONTROL_CHANNEL(1));
      LOGI("[BOOT][MASTER] CC-Link connect ret=%d, cost=%llu ms",
         cc_ret,
         (unsigned long long)(GetTickCount64() - boot_cclink_begin));
      if (cc_ret != 0) {
         LOGE("连接CC-Link失败.");
      }
      else {
@@ -231,11 +241,21 @@
      // 读缓存数据
      const ULONGLONG boot_cache_begin = GetTickCount64();
      const ULONGLONG boot_read_begin = GetTickCount64();
      readCache();
      LOGI("[BOOT][MASTER] readCache finished, cost=%llu ms", (unsigned long long)(GetTickCount64() - boot_read_begin));
      const ULONGLONG boot_state_begin = GetTickCount64();
      loadState();
      LOGI("[BOOT][MASTER] loadState finished, cost=%llu ms", (unsigned long long)(GetTickCount64() - boot_state_begin));
      if (m_listener.onControlJobChanged) {
         notifyControlJobChanged();
      }
      LOGI("[BOOT][MASTER] cache/state loaded, cost=%llu ms (since init %llu ms)",
         (unsigned long long)(GetTickCount64() - boot_cache_begin),
         (unsigned long long)(GetTickCount64() - boot_master_begin));
      // 定时器
@@ -258,7 +278,50 @@
      LOGI("<Master>初始化完成.");
      LOGI("[BOOT][MASTER] init finished, total cost=%llu ms",
         (unsigned long long)(GetTickCount64() - boot_master_begin));
      return 0;
   }
   void CMaster::setCurveMode(CurveMode mode)
   {
      if (m_curveMode == mode) {
         return;
      }
      m_curveMode = mode;
      if (m_pCollector != nullptr) {
         const uint32_t mids[] = {
            MID_Bonder1, MID_Bonder2,
            MID_VacuumBakeA, MID_VacuumBakeB,
            MID_BakeCoolingA, MID_BakeCoolingB
         };
         for (uint32_t mid : mids) {
            if (mode == CurveMode::EmptyChamber) {
               m_pCollector->batchStart(mid, "EMPTY_CHAMBER", 30 * 60 * 1000ULL); // 空腔模式:启动采样批次
            }
            else {
               m_pCollector->batchStop(mid);
               m_pCollector->buffersClear(mid); // 切回生产模式,清掉空腔数据
            }
         }
      }
      LOGI("<Master>CurveMode=%s", mode == CurveMode::EmptyChamber ? "EmptyChamber" : "Production");
   }
   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()
@@ -286,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();
@@ -688,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;
                     }
                  }
               }
            }
@@ -950,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; }
                  }
               }
            }
@@ -1675,6 +1761,15 @@
         }
      };
      listener.onSVDataReport = [&](void* pEquipment, void* pData) {
         const bool allowSvLog =
            (m_state == MASTERSTATE::RUNNING ||
               m_state == MASTERSTATE::RUNNING_CONTINUOUS_TRANSFER ||
               m_state == MASTERSTATE::RUNNING_BATCH ||
               m_state == MASTERSTATE::STARTING);
         const bool allowCurve = allowSvLog || (m_curveMode == CurveMode::EmptyChamber);
         if (!allowCurve) {
            return;
         }
         CSVData* pSVData = (CSVData*)pData;
         auto rawData = pSVData->getSVRawData();
         std::vector<CParam> params;
@@ -1726,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 的玻璃
@@ -1771,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()) {
@@ -1851,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);
@@ -2362,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"));
@@ -2411,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失败");
         }
      }
   }
@@ -2648,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*/)
   {
@@ -2757,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);
@@ -2820,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;
   }
@@ -3408,6 +3657,11 @@
      }
      m_pControlJob->abort(description);
      // 先上报一次状态变化(便于 PrJobAbort 触发)
      if (m_listener.onControlJobChanged) {
         notifyControlJobChanged();
      }
      // 释放Job相关
      for (auto item : m_processJobs) {
@@ -3492,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);
@@ -3580,14 +3843,14 @@
         auto& dataTypes = CServoUtilsTool::getEqDataTypes();
         auto& bonderTypes = dataTypes[MID_Bonder1];
         for (size_t i = 0; i < bonderTypes.size(); ++i) {
            m_pCollector->buffersSetChannelName(MID_Bonder1, i + 1, bonderTypes[i].c_str());
            m_pCollector->buffersSetChannelName(MID_Bonder2, i + 1, bonderTypes[i].c_str());
            m_pCollector->buffersSetChannelName(MID_Bonder1, (UINT)i + 1, bonderTypes[(UINT)i].c_str());
            m_pCollector->buffersSetChannelName(MID_Bonder2, (UINT)i + 1, bonderTypes[(UINT)i].c_str());
         }
         auto& vacuumbakeTypes = dataTypes[MID_VacuumBakeA];
         for (size_t i = 0; i < vacuumbakeTypes.size(); ++i) {
            m_pCollector->buffersSetChannelName(MID_VacuumBakeA, i + 1, vacuumbakeTypes[i].c_str());
            m_pCollector->buffersSetChannelName(MID_VacuumBakeB, i + 1, vacuumbakeTypes[i].c_str());
            m_pCollector->buffersSetChannelName(MID_VacuumBakeA, (UINT)i + 1, vacuumbakeTypes[(UINT)i].c_str());
            m_pCollector->buffersSetChannelName(MID_VacuumBakeB, (UINT)i + 1, vacuumbakeTypes[(UINT)i].c_str());
         }
         auto& coolingTypes = dataTypes[MID_BakeCoolingA];
@@ -3595,6 +3858,17 @@
            m_pCollector->buffersSetChannelName(MID_BakeCoolingA, i + 1, coolingTypes[i].c_str());
            m_pCollector->buffersSetChannelName(MID_BakeCoolingB, i + 1, coolingTypes[i].c_str());
         }
         if (m_curveMode == CurveMode::EmptyChamber) {
            const uint32_t mids[] = {
               MID_Bonder1, MID_Bonder2,
               MID_VacuumBakeA, MID_VacuumBakeB,
               MID_BakeCoolingA, MID_BakeCoolingB
            };
            for (uint32_t mid : mids) {
               m_pCollector->batchStart(mid, "EMPTY_CHAMBER", 10 * 60 * 1000ULL);
            }
         }
      }
   }