| | |
| | | m_hEventDispatchThreadExit[1] = nullptr; |
| | | } |
| | | |
| | | // 释放人工搬出缓冲区里的玻璃 |
| | | for (auto* pGlass : m_bufGlass) { |
| | | if (pGlass != nullptr) { |
| | | pGlass->release(); |
| | | } |
| | | } |
| | | m_bufGlass.clear(); |
| | | |
| | | DeleteCriticalSection(&m_criticalSection); |
| | | } |
| | |
| | | } |
| | | 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(); |
| | |
| | | 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 的玻璃 |
| | |
| | | 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()) { |
| | |
| | | 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); |
| | |
| | | |
| | | 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]; |
| | | } |
| | | |
| | | pPort->sendCassetteCtrlCmd(CCC_PROCESS_START, jobExistence, 12, slotProcess, jobCount, nullptr, nullptr); |
| | | return 0; |
| | | } |
| | | |
| | |
| | | } |
| | | m_pControlJob->abort(description); |
| | | |
| | | // 先上报一次状态变化(便于 PrJobAbort 触发) |
| | | if (m_listener.onControlJobChanged) { |
| | | notifyControlJobChanged(); |
| | | } |
| | | |
| | | |
| | | // 释放Job相关 |
| | | for (auto item : m_processJobs) { |