From a7ed48926d164942f65c1231ed336e24a74619b1 Mon Sep 17 00:00:00 2001
From: chenluhua1980 <Chenluhua@qq.com>
Date: 星期三, 07 一月 2026 09:00:28 +0800
Subject: [PATCH] 1.模拟测试继续

---
 SourceCode/Bond/Servo/CLoadPort.h   |    2 
 SourceCode/Bond/Servo/CMaster.cpp   |  166 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 SourceCode/Bond/Servo/CLoadPort.cpp |    5 +
 3 files changed, 173 insertions(+), 0 deletions(-)

diff --git a/SourceCode/Bond/Servo/CLoadPort.cpp b/SourceCode/Bond/Servo/CLoadPort.cpp
index c50245b..175d593 100644
--- a/SourceCode/Bond/Servo/CLoadPort.cpp
+++ b/SourceCode/Bond/Servo/CLoadPort.cpp
@@ -561,6 +561,11 @@
 		return m_portStatusReport.getCassetteId();
 	}
 
+	void CLoadPort::simulateSetCassetteId(const char* pszCarrierId)
+	{
+		m_portStatusReport.setCassetteId(pszCarrierId);
+	}
+
 	int CLoadPort::getLoadingCassetteType()
 	{
 		return m_portStatusReport.getLoadingCassetteType();
diff --git a/SourceCode/Bond/Servo/CLoadPort.h b/SourceCode/Bond/Servo/CLoadPort.h
index 91101d2..1fb70d7 100644
--- a/SourceCode/Bond/Servo/CLoadPort.h
+++ b/SourceCode/Bond/Servo/CLoadPort.h
@@ -56,6 +56,8 @@
 		int getPortStatus();
 		int getCassetteSequenceNo();
 		std::string& getCassetteId();
+		// Simulation helper: allow setting CarrierID when no EFEM is connected.
+		void simulateSetCassetteId(const char* pszCarrierId);
 		int getLoadingCassetteType();
 		int getQTimeFlag();
 		int getCassetteMappingState();
diff --git a/SourceCode/Bond/Servo/CMaster.cpp b/SourceCode/Bond/Servo/CMaster.cpp
index 95caa52..1ef96c5 100644
--- a/SourceCode/Bond/Servo/CMaster.cpp
+++ b/SourceCode/Bond/Servo/CMaster.cpp
@@ -2036,6 +2036,172 @@
 		}
 
 
+		// 妯℃嫙娴嬭瘯锛堟棤鏈哄櫒鑱旀満鏃剁敤浜庤仈璋� EAP锛�
+		// 璇诲彇 test.ini锛堝綋鍓嶇洰褰曟垨 exe 鍚岀洰褰曪級
+		{
+			struct SimCfg {
+				bool enabled{ false };
+				DWORD intervalMs{ 5000 };
+				int step{ 0 };
+			};
+			auto loadCfg = [&]() -> SimCfg {
+				SimCfg cfg;
+
+				// Try INI: current dir, then exe dir
+				char iniPath[MAX_PATH] = { 0 };
+				strcpy_s(iniPath, "test.ini");
+				auto readIni = [&](const char* path) -> bool {
+					const UINT en = GetPrivateProfileIntA("SimEap", "Enabled", 0, path);
+					if (en == 0) return false; // treat as missing/disabled
+					cfg.enabled = (en != 0);
+					cfg.intervalMs = (DWORD)GetPrivateProfileIntA("SimEap", "IntervalMs", 5000, path);
+					cfg.intervalMs = max(500u, cfg.intervalMs);
+					cfg.step = (int)GetPrivateProfileIntA("SimEap", "Step", 0, path);
+					return true;
+				};
+				if (!readIni(iniPath)) {
+					char exePath[MAX_PATH] = { 0 };
+					GetModuleFileNameA(NULL, exePath, MAX_PATH);
+					char* lastSlash = strrchr(exePath, '\\');
+					if (lastSlash != nullptr) {
+						*(lastSlash + 1) = '\0';
+						strcat_s(exePath, "test.ini");
+						readIni(exePath);
+					}
+				}
+				return cfg;
+			};
+
+			const SimCfg cfg = loadCfg();
+			if (cfg.enabled) {
+				static DWORD lastTick = 0;
+				static int lastExecutedStep = -1;
+				static bool inited = false;
+				static SERVO::CGlass simGlass;
+				static SERVO::CVcrEventReport simVcr;
+				static SERVO::CProcessJob simPj("SIM_PJ_001");
+				static SERVO::CControlJob simCj("SIM_CJ_001");
+
+				if (!inited) {
+					inited = true;
+					simGlass.setID("SIM_PANEL_001");
+					simVcr.getGlassId() = "SIM_PANEL_001";
+				}
+
+				DWORD now = GetTickCount();
+				if (lastTick == 0) lastTick = now;
+				if ((now - lastTick) < cfg.intervalMs) {
+					return;
+				}
+				lastTick = now;
+
+				// 鍗曟瑙﹀彂锛氭瘡涓� Step 鍙墽琛屼竴娆★紱浣犳墜鍔ㄤ慨鏀� ini 鐨� Step 鍊煎悗浼氬啀娆¤Е鍙�
+				const int step = cfg.step;
+				if (step <= 0 || step == lastExecutedStep) {
+					return;
+				}
+				lastExecutedStep = step;
+
+				// 鍙栦竴涓� LoadPort 浣滀负妯℃嫙鐩爣
+				SERVO::CLoadPort* pLpEq = (SERVO::CLoadPort*)getEquipment(EQ_ID_LOADPORT1);
+
+				auto fireLoadPortStatus = [&](short status) {
+					pLpEq->simulateSetCassetteId("Test-Cassette-001");
+					if (m_listener.onLoadPortStatusChanged != nullptr && pLpEq != nullptr) {
+						m_listener.onLoadPortStatusChanged(this, pLpEq, status, 0);
+					}
+				};
+				auto fireProcessState = [&](SERVO::CEquipment* pEq, int slotNo, SERVO::PROCESS_STATE st) {
+					if (m_listener.onProcessStateChanged != nullptr && pEq != nullptr) {
+						m_listener.onProcessStateChanged(this, pEq, slotNo, st);
+					}
+				};
+
+				LOGI("<Master>SIM_EAP single-step=%d", step);
+				switch (step) {
+					// ===== 涓氬姟娴佺▼姝ラ锛�1~23锛�=====
+				case 1: // E87_06 Material Arrived(TransferBlock) -> Port Blocked
+					fireLoadPortStatus(PORT_BLOCKED);
+					break;
+				case 2: // E87_03 CarrierID Readed -> Port InUse
+					fireLoadPortStatus(PORT_INUSE);
+					break;
+				case 3: // S1F3 Query CJ Space (Host->EQ) - wait host
+					LOGI("<Master>SIM_EAP step3: wait host S1F3");
+					break;
+				case 4: // S16F21 Query PJ Space (Host->EQ) - wait host
+					LOGI("<Master>SIM_EAP step4: wait host S16F21");
+					break;
+				case 5: // S7F19 Query PPID List (Host->EQ) - wait host
+					LOGI("<Master>SIM_EAP step5: wait host S7F19");
+					break;
+				case 6: // S3F17 ProceedWithCarrier (Host->EQ) - wait host
+					LOGI("<Master>SIM_EAP step6: wait host S3F17 ProceedWithCarrier");
+					break;
+				case 7: // E87_14 Check SlotMap (璁惧涓婃姤/杩涘叆 WFH) - 鐢� PORT_INUSE 鍐呴儴瑙﹀彂
+					fireLoadPortStatus(PORT_INUSE);
+					break;
+				case 8: // S3F17 ProceedWithSlotMap (Host->EQ) - wait host
+					LOGI("<Master>SIM_EAP step8: wait host S3F17 ProceedWithSlotMap");
+					break;
+				case 9: // SlotMap Verify OK (鏈」鐩湪鏀跺埌 ProceedWithSlotMap 鍚庝笂鎶�) - wait host
+					LOGI("<Master>SIM_EAP step9: wait host ProceedWithSlotMap to trigger VerifyOK");
+					break;
+				case 10: // Create PJ (Host->EQ) - wait host
+					LOGI("<Master>SIM_EAP step10: wait host S16F15 CreateMultiPJ");
+					break;
+				case 11: // PJ Queued锛堟湰椤圭洰鍦ㄥ垱寤� PJ 鍚庝笂鎶ワ級 - wait host
+					LOGI("<Master>SIM_EAP step11: wait host CreateMultiPJ to trigger PJ_Queued");
+					break;
+				case 12: // Create CJ (Host->EQ) - wait host
+					LOGI("<Master>SIM_EAP step12: wait host S14F9 CreateCJ");
+					break;
+				case 13: // CJ Start
+					if (m_listener.onCjStart != nullptr) m_listener.onCjStart(this, &simCj);
+					break;
+				case 14: // PJ Start
+					if (m_listener.onPjStart != nullptr) m_listener.onPjStart(this, &simPj);
+					break;
+				case 15: // OCR
+					if (m_listener.onEqVcrEventReport != nullptr && pLpEq != nullptr) {
+						m_listener.onEqVcrEventReport(this, pLpEq, &simVcr);
+					}
+					break;
+				case 16: // Panel Start
+					if (m_listener.onPanelStart != nullptr) m_listener.onPanelStart(this, &simGlass);
+					// 鍚屾椂瑙﹀彂涓�娆″瓙鏈哄彴寮�濮嬶紙绀轰緥锛欱onder1, slot 1锛�
+					fireProcessState(getEquipment(EQ_ID_Bonder1), 1, SERVO::PROCESS_STATE::Processing);
+					break;
+				case 17: // Panel End
+					// 鍚屾椂瑙﹀彂涓�娆″瓙鏈哄彴缁撴潫锛堢ず渚嬶細Bonder1, slot 1锛�
+					fireProcessState(getEquipment(EQ_ID_Bonder1), 1, SERVO::PROCESS_STATE::Complete);
+					if (m_listener.onPanelEnd != nullptr) m_listener.onPanelEnd(this, &simGlass);
+					break;
+				case 18: // PJ End
+					if (m_listener.onPjEnd != nullptr) m_listener.onPjEnd(this, &simPj);
+					break;
+				case 19: // CJ End
+					if (m_listener.onCjEnd != nullptr) m_listener.onCjEnd(this, &simCj);
+					break;
+				case 20: // Ready to Release (Port Unload Ready; with prev INUSE will also trigger ReadyToRelease)
+					fireLoadPortStatus(PORT_UNLOAD_READY);
+					break;
+				case 21: // CarrierRelease (Host->EQ) - optional / wait host
+					LOGI("<Master>SIM_EAP step21: wait host S3F17 CarrierRelease");
+					break;
+				case 22: // Ready to Unload
+					fireLoadPortStatus(PORT_UNLOAD_READY);
+					break;
+				case 23: // Material Removed (and ReadyToLoad)
+					fireLoadPortStatus(PORT_LOAD_READY);
+					fireLoadPortStatus(PORT_EMPTY); // will also raise LoadPortNotAssoc via Model
+					break;
+				default:
+					break;
+				}
+			}
+		}
+
 
 		// 妯℃嫙娴嬭瘯
 		/*

--
Gitblit v1.9.3