From 3e0ceaf4e569ea1f57a14de2f6135d1f1a50d080 Mon Sep 17 00:00:00 2001
From: mrDarker <mr.darker@163.com>
Date: 星期三, 27 八月 2025 13:47:31 +0800
Subject: [PATCH] Merge branch 'clh' into liuyang

---
 SourceCode/Bond/Servo/HsmsPassive.h                    |    2 
 SourceCode/Bond/Servo/resource.h                       |    0 
 SourceCode/Bond/Servo/HsmsPassive.cpp                  |   10 +
 SourceCode/Bond/Servo/Configuration.cpp                |    5 
 SourceCode/Bond/Servo/CMaster.h                        |   29 ++
 SourceCode/Bond/Servo/Model.cpp                        |    9 
 SourceCode/Bond/Servo/ServoDlg.cpp                     |   23 ++
 SourceCode/Bond/x64/Debug/VariableList.txt             |    3 
 SourceCode/Bond/Servo/Configuration.h                  |    1 
 SourceCode/Bond/Servo/TopToolbar.h                     |    1 
 SourceCode/Bond/x64/Debug/CollectionEventList.txt      |    2 
 SourceCode/Bond/Servo/CEquipment.cpp                   |    5 
 SourceCode/Bond/Servo/Servo.rc                         |    0 
 SourceCode/Bond/Servo/TopToolbar.cpp                   |   15 +
 SourceCode/Bond/Servo/CMaster.cpp                      |  477 ++++++++++++++++++++++++++++++++++++++++++++++-
 SourceCode/Bond/Servo/CEquipment.h                     |    2 
 SourceCode/Bond/x64/Debug/ReportList.txt               |    1 
 Document/Panel Bonder八零联合 SecsTest CheckList_v3.0.xlsx |    0 
 18 files changed, 568 insertions(+), 17 deletions(-)

diff --git "a/Document/Panel Bonder\345\205\253\351\233\266\350\201\224\345\220\210 SecsTest CheckList_v3.0.xlsx" "b/Document/Panel Bonder\345\205\253\351\233\266\350\201\224\345\220\210 SecsTest CheckList_v3.0.xlsx"
index 64eba06..59056de 100644
--- "a/Document/Panel Bonder\345\205\253\351\233\266\350\201\224\345\220\210 SecsTest CheckList_v3.0.xlsx"
+++ "b/Document/Panel Bonder\345\205\253\351\233\266\350\201\224\345\220\210 SecsTest CheckList_v3.0.xlsx"
Binary files differ
diff --git a/SourceCode/Bond/Servo/CEquipment.cpp b/SourceCode/Bond/Servo/CEquipment.cpp
index 7560957..bfdec30 100644
--- a/SourceCode/Bond/Servo/CEquipment.cpp
+++ b/SourceCode/Bond/Servo/CEquipment.cpp
@@ -1325,7 +1325,7 @@
 		return nullptr;
 	}
 
-	CSlot* CEquipment::getProcessedSlot(MaterialsType putSlotType)
+	CSlot* CEquipment::getProcessedSlot(MaterialsType putSlotType, BOOL bJobMode/* = FALSE*/)
 	{
 		for (int i = 0; i < SLOT_MAX; i++) {
 			if (!m_slot[i].isEnable()) continue;
@@ -1334,6 +1334,7 @@
 			if (!isSlotProcessed(i)) continue;
 			if (pGlass == nullptr) continue;
 			if (!pGlass->isScheduledForProcessing()) continue;
+			if (bJobMode && pGlass->getProcessJob() == nullptr) continue;
 			if(pGlass->getInspResult(m_nID, 0) == InspResult::Fail) continue;
 			int lsPath = m_slot[i].getLinkSignalPath();
 			if(!m_bLinkSignalToUpstream[lsPath][SIGNAL_UPSTREAM_INLINE]
@@ -1654,7 +1655,7 @@
 	int CEquipment::decodeVCREventReport(CStep* pStep, const char* pszData, size_t size)
 	{
 		CVcrEventReport vcrEventReport;
-		vcrEventReport.unserialize(pszData, size);
+		vcrEventReport.unserialize(pszData, (int)size);
 		LOGI("<CEquipment-%s>decodeVCREventReport<Result:%d, GlassId:%s>\n", m_strName.c_str(),
 			vcrEventReport.getVcrResult(),
 			vcrEventReport.getGlassId().c_str());
diff --git a/SourceCode/Bond/Servo/CEquipment.h b/SourceCode/Bond/Servo/CEquipment.h
index ce75a56..27a448e 100644
--- a/SourceCode/Bond/Servo/CEquipment.h
+++ b/SourceCode/Bond/Servo/CEquipment.h
@@ -185,7 +185,7 @@
 		CSlot* getNonEmptySlot(MaterialsType type);
 
 		// 获取一个指定物料类型(G1,G2,G1&G2)的且已经加工处理的槽位
-		CSlot* getProcessedSlot(MaterialsType putSlotType);
+		CSlot* getProcessedSlot(MaterialsType putSlotType, BOOL bJobMode = FALSE);
 		CSlot* getProcessedSlot2(MaterialsType putSlotType, const std::vector<int>& candidates);
 		CSlot* getInspFailSlot();
 		CSlot* getProcessedSlotCt(unsigned int slot);
diff --git a/SourceCode/Bond/Servo/CMaster.cpp b/SourceCode/Bond/Servo/CMaster.cpp
index e01b180..baf3806 100644
--- a/SourceCode/Bond/Servo/CMaster.cpp
+++ b/SourceCode/Bond/Servo/CMaster.cpp
@@ -55,9 +55,11 @@
 		m_pActiveRobotTask = nullptr;
 		m_nLastError = 0;
 		m_isCompareMapsBeforeProceeding = FALSE;
+		m_bJobMode = FALSE;
 		m_bEnableEventReport = true;
 		m_bEnableAlarmReport = true;
 		m_bContinuousTransfer = false;
+		m_bBatch = false;
 		m_nContinuousTransferCount = 0;
 		m_nContinuousTransferStep = CTStep_Unknow;
 		m_pControlJob = nullptr;
@@ -269,6 +271,7 @@
 		}
 
 		m_bContinuousTransfer = false;
+		m_bBatch = false;
 		setState(MASTERSTATE::STARTING);
 		m_ullStartTime = GetTickCount64();
 
@@ -282,6 +285,21 @@
 		}
 
 		m_bContinuousTransfer = true;
+		m_bBatch = false;
+		setState(MASTERSTATE::STARTING);
+		m_ullStartTime = GetTickCount64();
+
+		return 0;
+	}
+
+	int CMaster::startBatch()
+	{
+		if (m_state != MASTERSTATE::READY) {
+			return -1;
+		}
+
+		m_bContinuousTransfer = false;
+		m_bBatch = true;
 		setState(MASTERSTATE::STARTING);
 		m_ullStartTime = GetTickCount64();
 
@@ -291,12 +309,28 @@
 	int CMaster::stop()
 	{
 		// 运行时间为累加结果,本次停止时刷新;
-		if (m_state != MASTERSTATE::RUNNING && m_state != MASTERSTATE::RUNNING_CONTINUOUS_TRANSFER) {
+		lock();
+		if (m_state != MASTERSTATE::RUNNING && m_state != MASTERSTATE::RUNNING_CONTINUOUS_TRANSFER
+			&& m_state != MASTERSTATE::RUNNING_BATCH) {
+			unlock();
 			return -1;
 		}
-
 		m_ullRunTime += (GetTickCount64() - m_ullStartTime);
+		unlock();
+
+
+		// 更新状态
 		setState(MASTERSTATE::STOPPING);
+
+
+		// ControlJob暂停
+		lock();
+		if (m_pControlJob != nullptr) {
+			m_pControlJob->pause();
+			saveState();
+		}
+		unlock();
+
 
 		return 0;
 	}
@@ -310,7 +344,8 @@
 
 	ULONGLONG CMaster::getRunTime()
 	{
-		if (m_state == MASTERSTATE::RUNNING || m_state == MASTERSTATE::RUNNING_CONTINUOUS_TRANSFER)
+		if (m_state == MASTERSTATE::RUNNING || m_state == MASTERSTATE::RUNNING_CONTINUOUS_TRANSFER
+			|| m_state == MASTERSTATE::RUNNING_BATCH)
 			return m_ullRunTime + (GetTickCount64() - m_ullStartTime);
 		else
 			return m_ullRunTime;
@@ -470,18 +505,21 @@
 				}
 				
 				// 检查看是否都已经切换到START状态
+				/*
 				if (!bIomcOk[6]) {
 					unlock();
 					setState(MASTERSTATE::MSERROR);
 					continue;
 				}
-
+				*/
 
 				unlock();
-				if(!m_bContinuousTransfer)
-					setState(MASTERSTATE::RUNNING);
-				else 
+				if(m_bContinuousTransfer)
 					setState(MASTERSTATE::RUNNING_CONTINUOUS_TRANSFER);
+				else if (m_bBatch)
+					setState(MASTERSTATE::RUNNING_BATCH);
+				else
+					setState(MASTERSTATE::RUNNING);
 
 				continue;
 			}
@@ -684,7 +722,7 @@
 					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_pActiveRobotTask = createTransferTask(pLoadPorts[s], pAligner, primaryType, secondaryType, 1, m_bJobMode);
 						if (m_pActiveRobotTask != nullptr) {
 							pEFEM->setContext(m_pActiveRobotTask->getContext());
 							goto PORT_GET;
@@ -693,6 +731,233 @@
 				}
 
 PORT_GET:
+				CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
+
+
+				unlock();
+				continue;
+			}
+
+			// 批处理模式,最终以此为准,但先保留之前的单片模式
+			else if (m_state == MASTERSTATE::RUNNING_BATCH) {
+				// 首选检查有没有CControlJob, 状态等
+				if (m_pControlJob == nullptr) {
+					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) {
+					LOGI("<Master>ControlJob已经进入列队");
+					m_pControlJob->queue();
+				}
+				if (m_pControlJob->state() == CJState::Queued) {
+					LOGI("<Master>ControlJob已经启动");
+					m_pControlJob->start();
+				}
+				if (m_pControlJob->state() == CJState::Paused) {
+					LOGI("<Master>ControlJob已经恢复运行");
+					m_pControlJob->resume();
+				}
+
+
+				// 如果当前未选择CProcessJob, 选择一个
+				if (m_inProcesJobs.empty()) {
+					auto pj = acquireNextProcessJob();
+					if (pj != nullptr) {
+						m_inProcesJobs.push_back(pj);
+
+						// 这里上报PJ Start事件
+						if (m_listener.onPjStart != nullptr) {
+							m_listener.onPjStart(this, pj);
+						}
+					}
+				}
+				if (m_inProcesJobs.empty()) {
+					LOGI("<Master>选择当前ProcessJob失败!");
+					unlock();
+					continue;
+				}
+
+				// 如果当前没有Glass, 选择
+				if (m_queueGlasses.empty()) {
+					int nCount = acquireGlassToQueue();
+					LOGI("<Master>已加入 %d 块Glass到工艺列队!", nCount);
+				}
+
+
+				// 检测判断robot状态
+				RMDATA& rmd = pEFEM->getRobotMonitoringData();
+				if (rmd.status != ROBOT_STATUS::Idle && rmd.status != ROBOT_STATUS::Run) {
+					unlock();
+					continue;
+				}
+
+				if (m_pActiveRobotTask != nullptr) {
+					if (m_pActiveRobotTask->isPicked()) {
+						m_pActiveRobotTask->place();
+					}
+					unlock();
+					// 检测到当前有正在下午的任务,确保当前任务完成或中止后继续
+					// LOGI("检测到当前有正在下午的任务,确保当前任务完成或中止后继续...");
+					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;
+					}
+				}
+				else if ((pBonder1->canPlaceGlassInSlot(0) && !pBonder1->canPlaceGlassInSlot(1))
+					|| (pBonder2->canPlaceGlassInSlot(0) && !pBonder2->canPlaceGlassInSlot(1))) {
+					primaryType = MaterialsType::G2;
+					secondaryType = MaterialsType::G1;
+				}
+
+
+				// Measurement -> LoadPort
+				if (rmd.armState[0] || rmd.armState[1]) {
+					LOGI("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], primaryType, secondaryType);
+						if (m_pActiveRobotTask != nullptr) {
+							goto BATCH_PORT_PUT;
+						}
+					}
+				}
+
+			BATCH_PORT_PUT:
+				CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
+
+
+				// Measurement NG -> LoadPort
+				// NG回原位
+				if (!rmd.armState[1]) {
+					m_pActiveRobotTask = createTransferTask_restore(pMeasurement, pLoadPorts);
+					CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
+				}
+
+
+				// BakeCooling ->Measurement
+				if (!rmd.armState[0]) {
+					m_pActiveRobotTask = createTransferTask_bakecooling_to_measurement(pBakeCooling, pMeasurement);
+					CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
+				}
+
+
+				// BakeCooling内部
+				// Bake -> Cooling
+				if (!rmd.armState[0]) {
+					m_pActiveRobotTask = createTransferTask_bake_to_cooling(pBakeCooling);
+					CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
+				}
+
+
+				// 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);
+				}
+
+
+				// Fliper(G2) -> Bonder
+				auto pSrcSlot = pVacuumBake->getProcessedSlot(primaryType);
+				if (pSrcSlot != nullptr && !rmd.armState[1] && !pBonder1->hasBondClass()) {
+					m_pActiveRobotTask = createTransferTask(pFliper, pBonder1, primaryType, secondaryType, 2);
+					CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
+				}
+				if (pSrcSlot != nullptr && !rmd.armState[1] && !pBonder2->hasBondClass()) {
+					m_pActiveRobotTask = createTransferTask(pFliper, pBonder2, primaryType, secondaryType, 2);
+					CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
+				}
+
+
+				// VacuumBake(G1) -> Bonder
+				if (!rmd.armState[0] && !pBonder1->hasBondClass()) {
+					m_pActiveRobotTask = createTransferTask(pVacuumBake, pBonder1, primaryType, secondaryType);
+					CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
+				}
+
+				if (!rmd.armState[0] && !pBonder2->hasBondClass()) {
+					m_pActiveRobotTask = createTransferTask(pVacuumBake, pBonder2, primaryType, secondaryType);
+					CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
+				}
+
+
+				// Aligner -> Fliper(G2)
+				// Aligner -> VacuumBake(G1)
+				if (!rmd.armState[1]) {
+					m_pActiveRobotTask = createTransferTask(pAligner, pFliper, primaryType, secondaryType);
+					CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
+				}
+
+				if (!rmd.armState[0]) {
+					m_pActiveRobotTask = createTransferTask(pAligner, pVacuumBake, primaryType, secondaryType);
+					CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
+				}
+
+
+				// Aligner -> LoadPort
+				if (!rmd.armState[1]) {
+					m_pActiveRobotTask = createTransferTask_restore(pAligner, pLoadPorts);
+					CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
+				}
+
+
+				// 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);
+						if (m_pActiveRobotTask != nullptr) {
+							CGlass* pGlass = (CGlass*)m_pActiveRobotTask->getContext();
+							pEFEM->setContext(pGlass);
+							pGlass->start();
+							bool bMoved = glassFromQueueToInPorcess(pGlass);
+							if (bMoved) {
+								LOGI("<Master>Glass(%s)从等待列队到工艺列队转移成功.",
+									pGlass->getID().c_str());
+							}
+							else {
+								LOGE("<Master>Glass(%s)从等待列队到工艺列队转移失败.",
+									pGlass->getID().c_str());
+							}
+
+							goto BATCH_PORT_GET;
+						}
+					}
+				}
+
+			BATCH_PORT_GET:
 				CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
 
 
@@ -982,15 +1247,27 @@
 			BOOL bOk = FALSE;
 			lock();
 			if (m_pActiveRobotTask != nullptr) {
+				LOGI("<CMaster>onPreFethedOutJob 0001.");
 				if (m_pActiveRobotTask->getSrcPosition() == p->getID()) {
+					LOGI("<CMaster>onPreFethedOutJob 0002.");
 					CGlass* pGlass = p->getGlassFromSlot(m_pActiveRobotTask->getSrcSlot());
 					if (pGlass != nullptr) {
+						LOGI("<CMaster>onPreFethedOutJob 0003.");
 						CJobDataS* pJobDataS = pGlass->getJobDataS();
 						if (pJobDataS != nullptr
 							&& pJobDataS->getCassetteSequenceNo() == pJobDataB->getCassetteSequenceNo()
 							&& pJobDataS->getJobSequenceNo() == pJobDataB->getJobSequenceNo()) {
 							bOk = TRUE;
 							LOGI("<CMaster>onPreFethedOutJob, 已校验数据一致性.");
+						}
+						LOGI("<CMaster>onPreFethedOutJob 0004.");
+						if (pJobDataS != nullptr) {
+							LOGI("<CMaster>onPreFethedOutJob 0005. %d,%d,%d,%d", 
+								pJobDataS->getCassetteSequenceNo(),
+								pJobDataB->getCassetteSequenceNo(),
+								pJobDataS->getJobSequenceNo(),
+								pJobDataB->getJobSequenceNo()
+								);
 						}
 					}
 				}
@@ -1102,6 +1379,34 @@
 
 					LOGI("放片完成...");
 					// 完成此条搬送任务,但要把数据和消息上抛应用层
+
+					// 如果是搬送回从AOI搬送回Port, 则glass工艺完成
+					if (m_pActiveRobotTask->getSrcPosition() == EQ_ID_MEASUREMENT) {
+						CGlass* pGlass = (CGlass*)m_pActiveRobotTask->getContext();
+						pGlass->complete();
+						bool bMoved = glassFromInPorcessToComplete(pGlass);
+						if (bMoved) {
+							LOGI("<Master>Glass(%s)从工艺列队到完成列队转移成功.",
+								pGlass->getID().c_str());
+						}
+						else {
+							LOGE("<Master>Glass(%s)从工艺列队到完成列队转移失败.",
+								pGlass->getID().c_str());
+						}
+
+						// 检查PJ是否已经完成
+						CProcessJob* pJob = getGlassProcessJob((CGlass*)m_pActiveRobotTask->getContext());
+						if (pJob != nullptr && checkAndUpdatePjComplete(pJob)) {
+							LOGE("<Master>ProcessJob(%s)完成.",
+								pJob->id().c_str());
+							if (m_listener.onPjEnd != nullptr) {
+								m_listener.onPjEnd(this, pJob);
+							}
+						}
+					}
+
+
+
 					unlock();
 
 
@@ -1403,6 +1708,38 @@
 			}
 		}
 
+
+
+		// 模拟测试
+		/*
+		static int aaa = 0;
+		aaa++;
+		if (aaa % 30 == 0) {
+			if (!m_queueGlasses.empty()) {
+				CGlass* pGlass = m_queueGlasses.front();
+				pGlass->start();
+				glassFromQueueToInPorcess(pGlass);
+			}
+		}
+
+		if (aaa % 45 == 0) {
+			if (!m_inProcesGlasses.empty()) {
+				CGlass* pGlass = m_inProcesGlasses.front();
+				pGlass->complete();
+				glassFromInPorcessToComplete(pGlass);
+
+
+				CProcessJob* pJob = getGlassProcessJob(pGlass);
+				if (pJob != nullptr && checkAndUpdatePjComplete(pJob)) {
+					LOGE("<Master>ProcessJob(%s)完成.",
+						pJob->id().c_str());
+					if (m_listener.onPjEnd != nullptr) {
+						m_listener.onPjEnd(this, pJob);
+					}
+				}
+			}
+		}
+		*/
 	}
 
 	void CMaster::connectEquipments()
@@ -1567,7 +1904,7 @@
 	static int taskSeqNo = 0;
 	CRobotTask* CMaster::createTransferTask(CEquipment* pSrcEq, CEquipment* pTarEq,
 		MaterialsType primaryType/* = MaterialsType::G1*/, MaterialsType secondaryType/* = MaterialsType::G2*/,
-		int armNo/* = 1*/)
+		int armNo/* = 1*/, BOOL bJobMode/* = FALSE*/)
 	{
 		if (!pSrcEq->IsEnabled()) { 
 			return nullptr;
@@ -1576,10 +1913,10 @@
 		CRobotTask* pTask = nullptr;
 		CSlot* pSrcSlot, * pTarSlot;
 		pTarSlot = pTarEq->getAvailableSlotForGlass(primaryType);
-		pSrcSlot = pSrcEq->getProcessedSlot(primaryType);
+		pSrcSlot = pSrcEq->getProcessedSlot(primaryType, bJobMode);
 		if (pSrcSlot == nullptr || nullptr == pTarSlot) {
 			pTarSlot = pTarEq->getAvailableSlotForGlass(secondaryType);
-			pSrcSlot = pSrcEq->getProcessedSlot(secondaryType);
+			pSrcSlot = pSrcEq->getProcessedSlot(secondaryType, bJobMode);
 		}
 
 
@@ -1819,6 +2156,11 @@
 	void CMaster::setCompareMapsBeforeProceeding(BOOL bCompare)
 	{
 		m_isCompareMapsBeforeProceeding = bCompare;
+	}
+
+	void CMaster::setJobMode(BOOL bJobMode)
+	{
+		m_bJobMode = bJobMode;
 	}
 
 	void CMaster::datetimeSync(SYSTEMTIME& time)
@@ -2089,4 +2431,117 @@
 
 		return true;
 	}
+
+	CProcessJob* CMaster::acquireNextProcessJob()
+	{
+		auto& pjs = m_pControlJob->getPjs();
+		for (const auto pj : pjs) {
+			if (pj->state() == PJState::Queued) {
+				pj->start();
+			}
+			return pj;
+		}
+
+
+		return nullptr;
+	}
+
+	CGlass* CMaster::acquireNextGlass()
+	{
+		for (auto* pj : m_inProcesJobs) {
+			// 遍历 PJ 的 carriers 和 slots
+			for (auto& cs : pj->carriers()) {
+				for (auto ctx : cs.contexts) {
+					CGlass* pGlass = (CGlass*)ctx;
+					if (pGlass->state() == GlsState::NoState) {
+						pGlass->queue();
+						return pGlass;
+					}
+				}
+			}
+		}
+		return nullptr; // 没有可加工的 Glass
+	}
+
+	int CMaster::acquireGlassToQueue()
+	{
+		int nCount = 0;
+		for (auto* pj : m_inProcesJobs) {
+			// 遍历 PJ 的 carriers 和 slots
+			for (auto& cs : pj->carriers()) {
+				for (auto ctx : cs.contexts) {
+					CGlass* pGlass = (CGlass*)ctx;
+					if (pGlass->state() == GlsState::NoState) {
+						pGlass->queue();
+						if(addGlassToQueue(pGlass)) nCount++;
+					}
+				}
+			}
+		}
+		return nCount;
+	}
+
+	bool CMaster::addGlassToQueue(CGlass* pGlass)
+	{
+		for (auto g : m_queueGlasses) {
+			if (g == pGlass) return false;
+		}
+
+		m_queueGlasses.push_back(pGlass);
+		return true;
+	}
+
+	bool CMaster::glassFromQueueToInPorcess(CGlass* pGlass)
+	{
+		auto it = std::find(m_queueGlasses.begin(), m_queueGlasses.end(), pGlass); 
+		if (it != m_queueGlasses.end()) {
+			m_inProcesGlasses.push_back(*it);
+			m_queueGlasses.erase(it);
+			return true;
+		}
+		return false;
+	}
+
+	bool CMaster::glassFromInPorcessToComplete(CGlass* pGlass)
+	{
+		auto it = std::find(m_inProcesGlasses.begin(), m_inProcesGlasses.end(), pGlass);
+		if (it != m_inProcesGlasses.end()) {
+			m_completeGlasses.push_back(*it);
+			m_inProcesGlasses.erase(it);
+			return true;
+		}
+		return false;
+	}
+
+	bool CMaster::checkAndUpdatePjComplete(CProcessJob* pJob)
+	{
+		ASSERT(pJob);
+		auto state = pJob->state();
+		if (state != PJState::InProcess && state != PJState::Paused) return false;
+
+		for (auto c : pJob->carriers()) {
+			for (auto g : c.contexts) {
+				CGlass* pGlass = (CGlass*)g;
+				if (pGlass->state() != GlsState::Aborted
+					&& pGlass->state() != GlsState::Completed
+					&& pGlass->state() != GlsState::Failed) return false;
+			}
+		}
+
+		return pJob->complete();
+	}
+
+	CProcessJob* CMaster::getGlassProcessJob(CGlass* pGlass)
+	{
+		if (m_pControlJob == nullptr) return nullptr;
+		for (auto pj : m_pControlJob->getPjs()) {
+			for (auto c : pj->carriers()) {
+				for (auto g : c.contexts) {
+					if (g == pGlass) return pj;
+				}
+			}
+		}
+
+		return nullptr;
+	}
 }
diff --git a/SourceCode/Bond/Servo/CMaster.h b/SourceCode/Bond/Servo/CMaster.h
index e8c7236..631ff24 100644
--- a/SourceCode/Bond/Servo/CMaster.h
+++ b/SourceCode/Bond/Servo/CMaster.h
@@ -39,6 +39,7 @@
         STARTING,
         RUNNING,
         RUNNING_CONTINUOUS_TRANSFER,
+        RUNNING_BATCH,
         STOPPING,
         MSERROR
     };
@@ -52,6 +53,7 @@
     typedef std::function<void(void* pMaster, CRobotTask* pTask, int code)> ONROBOTTASKEVENT;
     typedef std::function<void(void* pMaster, CEquipment* pEquipment, short status, __int64 data)> ONLOADPORTSTATUSCHANGED;
     typedef std::function<void(void* pMaster, int round)> ONCTROUNDEND;
+    typedef std::function<void(void* pMaster, void* pj)> ONPJSTART;
     typedef struct _MasterListener
     {
         ONMASTERSTATECHANGED    onMasterStateChanged;
@@ -63,6 +65,8 @@
         ONROBOTTASKEVENT        onRobotTaskEvent;
         ONLOADPORTSTATUSCHANGED	onLoadPortStatusChanged;
         ONCTROUNDEND            onCTRoundEnd;
+        ONPJSTART               onPjStart;
+        ONPJSTART               onPjEnd;
     } MasterListener;
 
     class CMaster : public IResourceView
@@ -79,6 +83,7 @@
         int term();
         int start();
         int startContinuousTransfer();
+        int startBatch();
         int stop();
         void clearError();
         ULONGLONG getRunTime();
@@ -98,6 +103,7 @@
         void setPortCassetteType(unsigned int index, SERVO::CassetteType type);
         void setPortEnable(unsigned int index, BOOL bEnable);
         void setCompareMapsBeforeProceeding(BOOL bCompare);
+        void setJobMode(BOOL bJobMode);
         void datetimeSync(SYSTEMTIME& time);
         void enableEventReport(bool bEnable);
         void enableAlarmReport(bool bEnable);
@@ -137,7 +143,7 @@
         void setState(MASTERSTATE state);
         CRobotTask* createTransferTask(CEquipment* pSrcEq, CEquipment* pTarEq,
             MaterialsType primaryType = MaterialsType::G1, MaterialsType secondaryType = MaterialsType::G2,
-            int armNo = 1);
+            int armNo = 1, BOOL bJobMode = FALSE);
         CRobotTask* createTransferTask_bonder_to_bakecooling(CEquipment* pSrcEq, CEquipment* pTarEq);
         CRobotTask* createTransferTask_bake_to_cooling(CEquipment* pSrcEq);
         CRobotTask* createTransferTask_bakecooling_to_measurement(CEquipment* pSrcEq, CEquipment* pTarEq);
@@ -153,6 +159,18 @@
         bool slotUsable(const std::string& carrierId, uint16_t slot) const override;
         bool ceidDefined(uint32_t ceid) const override;
 
+    public:
+        // 新增函数
+        CProcessJob* acquireNextProcessJob();
+        CGlass* acquireNextGlass();
+        int acquireGlassToQueue();
+        bool addGlassToQueue(CGlass* pGlass);
+        bool glassFromQueueToInPorcess(CGlass* pGlass);
+        bool glassFromInPorcessToComplete(CGlass* pGlass);
+        bool checkAndUpdatePjComplete(CProcessJob* pJob);
+        CProcessJob* getGlassProcessJob(CGlass* pGlass);
+
+
     private:
         CRITICAL_SECTION m_criticalSection;
         MasterListener m_listener;
@@ -161,6 +179,7 @@
         std::string m_strFilepath;
         BOOL m_bDataModify;
         bool m_bContinuousTransfer;
+        bool m_bBatch;
 
     private:
         /* 监控比特位的线程*/
@@ -189,11 +208,19 @@
 
         // 在开始工艺前是否先需要先比较map
         BOOL m_isCompareMapsBeforeProceeding;
+        BOOL m_bJobMode;
+
 
         // 千传圈数计数
         int m_nContinuousTransferCount;
         int m_nContinuousTransferStep;
 
+        // 新增已经开始处理的ProcessJob列表
+        std::vector<CProcessJob*> m_inProcesJobs;
+        std::vector<CGlass*> m_queueGlasses;
+        std::vector<CGlass*> m_inProcesGlasses;
+        std::vector<CGlass*> m_completeGlasses;
+
     private:
         bool m_bEnableEventReport;
         bool m_bEnableAlarmReport;
diff --git a/SourceCode/Bond/Servo/Configuration.cpp b/SourceCode/Bond/Servo/Configuration.cpp
index 18ee806..c853bba 100644
--- a/SourceCode/Bond/Servo/Configuration.cpp
+++ b/SourceCode/Bond/Servo/Configuration.cpp
@@ -160,6 +160,11 @@
 	return GetPrivateProfileInt(_T("Master"), _T("CompareMapsBeforeProceeding"), 0, m_strFilepath);
 }
 
+BOOL CConfiguration::isJobMode()
+{
+	return GetPrivateProfileInt(_T("Master"), _T("JobMode"), 0, m_strFilepath);
+}
+
 void CConfiguration::setContinuousTransferCount(int round)
 {
 	WritePrivateProfileString(_T("Master"), _T("CTRound"),
diff --git a/SourceCode/Bond/Servo/Configuration.h b/SourceCode/Bond/Servo/Configuration.h
index 96f0e7f..a1e06da 100644
--- a/SourceCode/Bond/Servo/Configuration.h
+++ b/SourceCode/Bond/Servo/Configuration.h
@@ -27,6 +27,7 @@
 	BOOL setPortCassetteType(unsigned int index, int cassetteType);
 	BOOL setPortEnable(unsigned int index, BOOL bEnable);
 	BOOL isCompareMapsBeforeProceeding();
+	BOOL isJobMode();
 	void setContinuousTransferCount(int round);
 	int getContinuousTransferCount();
 
diff --git a/SourceCode/Bond/Servo/HsmsPassive.cpp b/SourceCode/Bond/Servo/HsmsPassive.cpp
index 689920b..e96e8a8 100644
--- a/SourceCode/Bond/Servo/HsmsPassive.cpp
+++ b/SourceCode/Bond/Servo/HsmsPassive.cpp
@@ -1879,6 +1879,16 @@
 	return requestEventReportSend("PJ_Queued");
 }
 
+int CHsmsPassive::requestEventReportSend_PJ_Start()
+{
+	return requestEventReportSend("PJ_Start");
+}
+
+int CHsmsPassive::requestEventReportSend_PJ_End()
+{
+	return requestEventReportSend("PJ_End");
+}
+
 
 
 
diff --git a/SourceCode/Bond/Servo/HsmsPassive.h b/SourceCode/Bond/Servo/HsmsPassive.h
index 0f5a636..f423b9e 100644
--- a/SourceCode/Bond/Servo/HsmsPassive.h
+++ b/SourceCode/Bond/Servo/HsmsPassive.h
@@ -192,6 +192,8 @@
 	int requestEventReportSend(const char* pszEventName);
 	int requestEventReportSend_CarrierID_Readed();
 	int requestEventReportSend_PJ_Queued();
+	int requestEventReportSend_PJ_Start();
+	int requestEventReportSend_PJ_End();
 
 private:
 	void replyAck(int s, int f, unsigned int systemBytes, BYTE ack, const char* pszAckName);
diff --git a/SourceCode/Bond/Servo/Model.cpp b/SourceCode/Bond/Servo/Model.cpp
index 8b854ee..b71d072 100644
--- a/SourceCode/Bond/Servo/Model.cpp
+++ b/SourceCode/Bond/Servo/Model.cpp
@@ -387,6 +387,14 @@
 	masterListener.onCTRoundEnd = [&](void* pMaster, int round) {
 		m_configuration.setContinuousTransferCount(round);
 	};
+	masterListener.onPjStart = [&](void* pMaster, void* pj) {
+		m_hsmsPassive.setVariableValue("PJStartID", ((SERVO::CProcessJob*)pj)->id().c_str());
+		m_hsmsPassive.requestEventReportSend_PJ_Start();
+	};
+	masterListener.onPjEnd = [&](void* pMaster, void* pj) {
+		m_hsmsPassive.setVariableValue("PJEndID", ((SERVO::CProcessJob*)pj)->id().c_str());
+		m_hsmsPassive.requestEventReportSend_PJ_End();
+	};
 	m_master.setListener(masterListener);
 	m_master.setContinuousTransferCount(m_configuration.getContinuousTransferCount());
 
@@ -396,6 +404,7 @@
 	strMasterDataFile.Format(_T("%s\\Master.dat"), (LPTSTR)(LPCTSTR)m_strWorkDir);
 	m_master.setCacheFilepath((LPTSTR)(LPCTSTR)strMasterDataFile);
 	m_master.setCompareMapsBeforeProceeding(m_configuration.isCompareMapsBeforeProceeding());
+	m_master.setJobMode(m_configuration.isJobMode());
 
 	// 加截Job
 	strMasterDataFile.Format(_T("%s\\MasterState.dat"), (LPTSTR)(LPCTSTR)m_strWorkDir);
diff --git a/SourceCode/Bond/Servo/Servo.rc b/SourceCode/Bond/Servo/Servo.rc
index ac1bebb..d8c898b 100644
--- a/SourceCode/Bond/Servo/Servo.rc
+++ b/SourceCode/Bond/Servo/Servo.rc
Binary files differ
diff --git a/SourceCode/Bond/Servo/ServoDlg.cpp b/SourceCode/Bond/Servo/ServoDlg.cpp
index 400712c..5d2e6d5 100644
--- a/SourceCode/Bond/Servo/ServoDlg.cpp
+++ b/SourceCode/Bond/Servo/ServoDlg.cpp
@@ -192,6 +192,7 @@
 				SERVO::MASTERSTATE state = theApp.m_model.getMaster().getState();
 				if (state == SERVO::MASTERSTATE::READY) {
 					m_pTopToolbar->GetBtn(IDC_BUTTON_RUN)->EnableWindow(TRUE);
+					m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_BATCH)->EnableWindow(TRUE);
 					m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_CT)->EnableWindow(TRUE);
 					m_pTopToolbar->GetBtn(IDC_BUTTON_STOP)->EnableWindow(FALSE);
 					m_pMyStatusbar->setBackgroundColor(STATUSBAR_BK_NORMAL);
@@ -208,14 +209,17 @@
 				}
 				else if (state == SERVO::MASTERSTATE::MSERROR) {
 					m_pTopToolbar->GetBtn(IDC_BUTTON_RUN)->EnableWindow(TRUE);
+					m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_BATCH)->EnableWindow(TRUE);
 					m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_CT)->EnableWindow(TRUE);
 					m_pTopToolbar->GetBtn(IDC_BUTTON_STOP)->EnableWindow(FALSE);
 					m_pMyStatusbar->setBackgroundColor(STATUSBAR_BK_ALARM);
 					m_pMyStatusbar->setForegroundColor(RGB(0, 0, 0));
 					m_pMyStatusbar->setRunTimeText("启动失败.");
 				}
-				else if (state == SERVO::MASTERSTATE::RUNNING || state == SERVO::MASTERSTATE::RUNNING_CONTINUOUS_TRANSFER) {
+				else if (state == SERVO::MASTERSTATE::RUNNING || state == SERVO::MASTERSTATE::RUNNING_CONTINUOUS_TRANSFER
+					|| state == SERVO::MASTERSTATE::RUNNING_BATCH) {
 					m_pTopToolbar->GetBtn(IDC_BUTTON_RUN)->EnableWindow(FALSE);
+					m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_BATCH)->EnableWindow(FALSE);
 					m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_CT)->EnableWindow(FALSE);
 					m_pTopToolbar->GetBtn(IDC_BUTTON_STOP)->EnableWindow(TRUE);
 					m_pMyStatusbar->setBackgroundColor(STATUSBAR_BK_RUNNING);
@@ -959,6 +963,19 @@
 		else {
 			if (theApp.m_model.getMaster().start() == 0) {
 				m_pTopToolbar->GetBtn(IDC_BUTTON_RUN)->EnableWindow(FALSE);
+				m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_BATCH)->EnableWindow(FALSE);
+				m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_CT)->EnableWindow(FALSE);
+			}
+		}
+	}
+	else if (id == IDC_BUTTON_RUN_BATCH) {
+		if (theApp.m_model.getMaster().getState() == SERVO::MASTERSTATE::MSERROR) {
+			AfxMessageBox("当前有机台发生错误,不能启动,请确认解决问题后再尝试重新启动!");
+		}
+		else {
+			if (theApp.m_model.getMaster().startBatch() == 0) {
+				m_pTopToolbar->GetBtn(IDC_BUTTON_RUN)->EnableWindow(FALSE);
+				m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_BATCH)->EnableWindow(FALSE);
 				m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_CT)->EnableWindow(FALSE);
 			}
 		}
@@ -970,6 +987,7 @@
 		else {
 			if (theApp.m_model.getMaster().startContinuousTransfer() == 0) {
 				m_pTopToolbar->GetBtn(IDC_BUTTON_RUN)->EnableWindow(FALSE);
+				m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_BATCH)->EnableWindow(FALSE);
 				m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_CT)->EnableWindow(FALSE);
 			}
 		}
@@ -1099,6 +1117,9 @@
 	else if (state == SERVO::MASTERSTATE::RUNNING_CONTINUOUS_TRANSFER) {
 		strText.Format(_T("千传模式:%02d:%02d:%02d   %s"), h, m, s, pszSuffix);
 	}
+	else if (state == SERVO::MASTERSTATE::RUNNING_BATCH) {
+		strText.Format(_T("JOB模式:%02d:%02d:%02d   %s"), h, m, s, pszSuffix);
+	}
 	else {
 		strText.Format(_T("已运行:%02d:%02d:%02d   %s"), h, m, s, pszSuffix);
 	}
diff --git a/SourceCode/Bond/Servo/TopToolbar.cpp b/SourceCode/Bond/Servo/TopToolbar.cpp
index a1d80db..4b7755e 100644
--- a/SourceCode/Bond/Servo/TopToolbar.cpp
+++ b/SourceCode/Bond/Servo/TopToolbar.cpp
@@ -8,6 +8,9 @@
 #include "Common.h"
 
 
+#define SHOW_BATCH	1
+#define SHOW_CT		1
+
 // CTopToolbar 对话框
 
 IMPLEMENT_DYNAMIC(CTopToolbar, CDialogEx)
@@ -27,6 +30,7 @@
 {
 	CDialogEx::DoDataExchange(pDX);
 	DDX_Control(pDX, IDC_BUTTON_RUN, m_btnRun);
+	DDX_Control(pDX, IDC_BUTTON_RUN_BATCH, m_btnRunBatch);
 	DDX_Control(pDX, IDC_BUTTON_RUN_CT, m_btnRunCt);
 	DDX_Control(pDX, IDC_BUTTON_STOP, m_btnStop);
 	DDX_Control(pDX, IDC_BUTTON_JOBS, m_btnCJobs);
@@ -54,6 +58,7 @@
 	CDialogEx::OnInitDialog();
 
 	InitBtn(m_btnRun, "Run_High_32.ico", "Run_Gray_32.ico");
+	InitBtn(m_btnRunBatch, "Run_High_32.ico", "Run_Gray_32.ico");
 	InitBtn(m_btnRunCt, "RunCt_High_32.ico", "RunCt_Gray_32.ico");
 	InitBtn(m_btnStop, "Stop_High_32.ico", "Stop_Gray_32.ico");
 	InitBtn(m_btnAlarm, "Alarm_o_32.ico", "Alarm_gray_32.ico");
@@ -120,10 +125,19 @@
 	x += BTN_WIDTH;
 	x += 2;
 
+#ifdef SHOW_BATCH
+	pItem = GetDlgItem(IDC_BUTTON_RUN_BATCH);
+	pItem->MoveWindow(x, y, BTN_WIDTH, nBthHeight);
+	x += BTN_WIDTH;
+	x += 2;
+#endif
+
+#ifdef SHOW_CT
 	pItem = GetDlgItem(IDC_BUTTON_RUN_CT);
 	pItem->MoveWindow(x, y, BTN_WIDTH, nBthHeight);
 	x += BTN_WIDTH;
 	x += 2;
+#endif
 
 	pItem = GetDlgItem(IDC_BUTTON_STOP);
 	pItem->MoveWindow(x, y, BTN_WIDTH, nBthHeight);
@@ -204,6 +218,7 @@
 {
 	switch (LOWORD(wParam)) {
 	case IDC_BUTTON_RUN:
+	case IDC_BUTTON_RUN_BATCH:
 	case IDC_BUTTON_RUN_CT:
 	case IDC_BUTTON_STOP:
 	case IDC_BUTTON_JOBS:
diff --git a/SourceCode/Bond/Servo/TopToolbar.h b/SourceCode/Bond/Servo/TopToolbar.h
index d93c9e6..7117c12 100644
--- a/SourceCode/Bond/Servo/TopToolbar.h
+++ b/SourceCode/Bond/Servo/TopToolbar.h
@@ -31,6 +31,7 @@
 
 private:
 	CBlButton m_btnRun;
+	CBlButton m_btnRunBatch;
 	CBlButton m_btnRunCt;
 	CBlButton m_btnStop;
 	CBlButton m_btnCJobs;
diff --git a/SourceCode/Bond/Servo/resource.h b/SourceCode/Bond/Servo/resource.h
index c911fc0..6dad237 100644
--- a/SourceCode/Bond/Servo/resource.h
+++ b/SourceCode/Bond/Servo/resource.h
Binary files differ
diff --git a/SourceCode/Bond/x64/Debug/CollectionEventList.txt b/SourceCode/Bond/x64/Debug/CollectionEventList.txt
index 44350fa..7d2dd32 100644
--- a/SourceCode/Bond/x64/Debug/CollectionEventList.txt
+++ b/SourceCode/Bond/x64/Debug/CollectionEventList.txt
@@ -39,3 +39,5 @@
 40001,E90_SPSM_InProcess_To_ProcessCompleted,,(40000)
 50000,CarrierID_Readed,,(50000)
 50001,PJ_Queued,,(50001)
+50002,PJ_Start,,(50002)
+50002,PJ_End,,(50003)
diff --git a/SourceCode/Bond/x64/Debug/ReportList.txt b/SourceCode/Bond/x64/Debug/ReportList.txt
index ce240e3..5e9be61 100644
--- a/SourceCode/Bond/x64/Debug/ReportList.txt
+++ b/SourceCode/Bond/x64/Debug/ReportList.txt
@@ -17,4 +17,5 @@
 40000,(1,10203,20000)
 50000,(5000)
 50001,(5003)
+50002,(5004)
 
diff --git a/SourceCode/Bond/x64/Debug/VariableList.txt b/SourceCode/Bond/x64/Debug/VariableList.txt
index 02fd14e..84f4e7a 100644
--- a/SourceCode/Bond/x64/Debug/VariableList.txt
+++ b/SourceCode/Bond/x64/Debug/VariableList.txt
@@ -37,4 +37,5 @@
 5000,CarrierID,A20,卡匣ID
 5001,CJobSpace,U1,CJ Space
 5002,PJobSpace,U1,PJ Space
-5003,PJQueued,L,PJ Queued
\ No newline at end of file
+5003,PJQueued,L,PJ Queued
+5004,PJStartID,A20,PJStartID
\ No newline at end of file

--
Gitblit v1.9.3