// 测试:默认识别规则(红框)识别用户户型图内红框柜子并报价 // 运行:node debug_redbox_recognition.js const { chromium } = require('playwright-core'); const fs = require('fs'); const IMG = 'd:/AI文件夹/预算报价/test_redbox_input.png'; const URL = 'http://127.0.0.1:8931/AI%E5%BF%AB%E9%80%9F%E6%8A%A5%E4%BB%B7_Demo2.optimizing.html?v=redbox-test'; const log = o => { fs.writeFileSync('debug_redbox_result.json', JSON.stringify(o, null, 1)); console.log(JSON.stringify(o, null, 1)); }; (async () => { const stage = s => fs.writeFileSync('debug_redbox_stage.txt', s + ' @ ' + new Date().toISOString()); process.on('exit', code => { try { fs.appendFileSync('debug_redbox_stage.txt', '\nexit:' + code); } catch (e) {} }); process.on('uncaughtException', e => { try { fs.appendFileSync('debug_redbox_stage.txt', '\nuncaught:' + e.message); } catch (e2) {} }); process.on('unhandledRejection', e => { try { fs.appendFileSync('debug_redbox_stage.txt', '\nrejection:' + String(e && e.message || e).slice(0, 300)); } catch (e2) {} }); stage('launching'); const b = await chromium.launchPersistentContext('d:/AI文件夹/预算报价/.edge-profile-' + Date.now(), { headless: true, executablePath: 'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe', args: ['--no-first-run', '--no-default-browser-check', '--no-sandbox', '--disable-gpu', '--disable-breakpad', '--disable-crash-reporter'] }); b.on('close', () => { try { fs.appendFileSync('debug_redbox_stage.txt', '\nbrowser-close @ ' + new Date().toISOString()); } catch (e) {} }); stage('launched'); const p = await b.newPage({ viewport: { width: 1400, height: 950 } }); // 只放行本地服务与 data/blob,阻断外部网络(qwen 等调用按设计静默回退本地识别) await p.route('**/*', r => { const u = r.request().url(); if (u.startsWith('http://127.0.0.1') || u.startsWith('data:') || u.startsWith('blob:')) return r.continue(); return r.abort(); }); const errors = []; const consoleLog = []; p.on('pageerror', e => errors.push('pageerror: ' + e.message)); p.on('console', m => { consoleLog.push(m.type() + ': ' + m.text().slice(0, 200)); if (consoleLog.length > 150) consoleLog.shift(); }); await p.goto(URL, { waitUntil: 'load' }); await p.evaluate(() => { try { localStorage.clear(); } catch (e) {} }); await p.reload({ waitUntil: 'load' }); await p.waitForTimeout(1000); stage('loaded'); // 1) 默认识别规则应为红框 stage('pre-rule'); const rule = await p.evaluate(() => ({ color: window.qCadRuleColor, name: window.qCadRuleColorName, mode: state.quoteMode })); stage('rule:' + rule.color); // 2) 上传户型图(统一上传入口,图片自动路由到图纸处理) await p.setInputFiles('#q-file-unified', IMG); stage('uploaded'); // 3) 轮询等待识别完成(每10秒记录进度,最长300秒) let done = false, diag = null; for (let i = 0; i < 30; i++) { await p.waitForTimeout(10000); diag = await p.evaluate(() => ({ hasRecog: !!(window.state && state.qRecogResult), boxes: (state.qRecogResult && state.qRecogResult.boxes) ? state.qRecogResult.boxes.length : -1, qwen: state.qRecogResult ? state.qRecogResult.qwen : null, cabs: window.qAIPlacedCabinets ? qAIPlacedCabinets.length : -1 })).catch(e => ({ pollErr: String(e.message).slice(0, 120) })); fs.appendFileSync('debug_redbox_stage.txt', '\npoll' + i + ': ' + JSON.stringify(diag)); if (diag && diag.cabs > 0) { done = true; break; } } if (!done) { const tail = await p.evaluate(() => { const el = document.getElementById('q-ai-log'); return el ? el.innerText.slice(-1500) : null; }).catch(() => null); log({ fail: 'recognition not done in 300s', diag, logTail: tail, consoleTail: consoleLog.slice(-40), errors }); await b.close(); process.exit(1); } await p.waitForTimeout(800); stage('recognized'); const recog = await p.evaluate(() => ({ ratio: state.qRecogResult.ratio, ratioSource: state.qRecogResult.source, dimCount: state.qRecogResult.dimCount, labelCount: state.qRecogResult.labelCount, qwen: state.qRecogResult.qwen, qwenNamed: state.qRecogResult.qwenNamed, applied: state.qRecogResult.applied, boxes: state.qRecogResult.boxes.map(b => ({ x: b.x, y: b.y, w: b.w, h: b.h, wm: b.wm, hm: b.hm })), cabinets: qAIPlacedCabinets.map(c => ({ name: c.type, width: c.width, height: c.height, source: c.source, pos: c.posLabel || '', kitchenWalls: c.kitchenWalls || null })), rooms: qRooms.map(r => ({ room: r.roomName, cab: r.cabinetType, w: r.cabinetWidth, h: r.cabinetHeight, area: +(+r.projectArea).toFixed(2) })) })); await p.screenshot({ path: 'debug_redbox_step1.png' }); // 4) 进入计价与报价单 await p.evaluate(() => goToStep(3)); await p.waitForTimeout(800); await p.evaluate(() => goToStep(4)); await p.waitForTimeout(1500); stage('step4'); const quote = await p.evaluate(() => { const t = id => { const el = document.getElementById(id); return el ? el.textContent.trim() : null; }; const s4 = document.getElementById('step-4'); return { grandTotal: t('q-grand-total-value'), grandArea: t('q-grand-total-area'), grandNote: t('q-grand-total-note'), step4Text: s4 ? s4.innerText.replace(/\n{2,}/g, '\n').slice(0, 2500) : null }; }); await p.screenshot({ path: 'debug_redbox_step4.png', fullPage: true }); log({ rule, recog, quote, errors }); await b.close(); })().catch(e => { log({ fail: String(e && e.message || e), stack: String(e && e.stack || '').slice(0, 500) }); process.exit(1); });