chenluhua1980
2026-01-07 a7ed48926d164942f65c1231ed336e24a74619b1
1.模拟测试继续
已修改3个文件
173 ■■■■■ 文件已修改
SourceCode/Bond/Servo/CLoadPort.cpp 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CLoadPort.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CMaster.cpp 166 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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();
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();
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);
                    // 同时触发一次子机台开始(示例:Bonder1, 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;
                }
            }
        }
        // 模拟测试
        /*