// CAE Mesh Generator - Main JavaScript class MeshGeneratorApp { constructor() { this.uploadedFile = null; this.processingStatus = null; this.statusPollingInterval = null; this.init(); } init() { this.setupEventListeners(); this.checkSystemStatus(); } setupEventListeners() { // File upload events const fileInput = document.getElementById('file-input'); const uploadArea = document.getElementById('upload-area'); if (fileInput) fileInput.addEventListener('change', (e) => this.handleFileSelect(e)); // Drag and drop events if (uploadArea) { uploadArea.addEventListener('dragover', (e) => this.handleDragOver(e)); uploadArea.addEventListener('dragleave', (e) => this.handleDragLeave(e)); uploadArea.addEventListener('drop', (e) => this.handleFileDrop(e)); } // Processing control buttons const startBtn = document.getElementById('start-processing'); const pauseBtn = document.getElementById('pause-processing'); const stopBtn = document.getElementById('stop-processing'); if (startBtn) startBtn.addEventListener('click', () => this.startProcessing()); if (pauseBtn) pauseBtn.addEventListener('click', () => this.pauseProcessing()); if (stopBtn) stopBtn.addEventListener('click', () => this.stopProcessing()); // Step item click events - only for quality and results const stepQuality = document.getElementById('step-quality'); const stepResults = document.getElementById('step-results'); if (stepQuality) stepQuality.addEventListener('click', () => this.handleStepClick('quality')); if (stepResults) stepResults.addEventListener('click', () => this.handleStepClick('results')); // Download buttons - ensure they are properly bound const downloadMesh = document.getElementById('download-mesh'); const downloadReport = document.getElementById('download-report'); const downloadImage = document.getElementById('download-image'); console.log('Setting up download button event listeners...'); console.log('Download mesh button:', downloadMesh); console.log('Download report button:', downloadReport); console.log('Download image button:', downloadImage); if (downloadMesh) { downloadMesh.addEventListener('click', (e) => { console.log('Download mesh button clicked'); e.preventDefault(); this.downloadMesh(); }); } else { console.error('Download mesh button not found!'); } if (downloadReport) { downloadReport.addEventListener('click', (e) => { console.log('Download report button clicked'); e.preventDefault(); this.downloadReport(); }); } else { console.error('Download report button not found!'); } if (downloadImage) { downloadImage.addEventListener('click', (e) => { console.log('Download image button clicked'); e.preventDefault(); this.downloadImage(); }); } else { console.error('Download image button not found!'); } // Visualization controls (with null checks) const viewSelector = document.getElementById('view-selector'); const refreshVisualization = document.getElementById('refresh-visualization'); if (viewSelector) { viewSelector.addEventListener('change', (e) => this.changeView(e.target.value)); } if (refreshVisualization) { refreshVisualization.addEventListener('click', () => this.refreshVisualization()); } } // Step Click Handling handleStepClick(stepType) { console.log(`Step clicked: ${stepType}`); switch(stepType) { case 'process': if (this.uploadedFile && !this.isProcessing()) { this.showAlert('信息', '开始网格生成...', 'info'); this.startProcessing(); } else if (!this.uploadedFile) { this.showAlert('提示', '请先上传STEP文件', 'warning'); } else if (this.isProcessing()) { this.showAlert('提示', '网格生成正在进行中...', 'info'); } break; case 'quality': this.showAlert('信息', '网格生成完成后才能查看质量检查结果', 'info'); break; case 'results': this.showAlert('信息', '网格生成完成后才能下载结果', 'info'); break; } } isProcessing() { return this.statusPollingInterval !== null; } // File Upload Handling handleFileSelect(event) { const file = event.target.files[0]; if (file) { this.validateAndUploadFile(file); } } handleDragOver(event) { event.preventDefault(); event.stopPropagation(); const uploadArea = document.getElementById('upload-area'); if (uploadArea) uploadArea.classList.add('dragover'); } handleDragLeave(event) { event.preventDefault(); event.stopPropagation(); const uploadArea = document.getElementById('upload-area'); if (uploadArea) uploadArea.classList.remove('dragover'); } handleFileDrop(event) { event.preventDefault(); event.stopPropagation(); const uploadArea = document.getElementById('upload-area'); if (uploadArea) uploadArea.classList.remove('dragover'); const files = event.dataTransfer.files; if (files.length > 0) { this.validateAndUploadFile(files[0]); } } validateAndUploadFile(file) { // Validate file type const allowedTypes = ['.step', '.stp']; const fileExtension = '.' + file.name.split('.').pop().toLowerCase(); if (!allowedTypes.includes(fileExtension)) { this.showAlert('错误', '请选择STEP格式文件 (.step 或 .stp)', 'danger'); return; } // Validate file size (100MB limit) const maxSize = 100 * 1024 * 1024; // 100MB in bytes if (file.size > maxSize) { this.showAlert('错误', '文件大小不能超过100MB', 'danger'); return; } this.uploadFile(file); } async uploadFile(file) { const formData = new FormData(); formData.append('file', file); try { this.showFileInfo(file, '上传中...'); this.updateStepStatus('upload', 'active'); const response = await fetch('/api/upload', { method: 'POST', body: formData }); const result = await response.json(); if (response.ok) { this.uploadedFile = file; this.showFileInfo(file, '上传成功'); this.updateStepStatus('upload', 'completed'); this.updateStepStatus('validate', 'completed'); this.enableProcessingControls(); this.showProcessingSection(); this.showAlert('成功', '文件上传成功!可以开始网格生成。', 'success'); } else { throw new Error(result.error || '上传失败'); } } catch (error) { console.error('Upload error:', error); this.showAlert('错误', `文件上传失败: ${error.message}`, 'danger'); this.updateStepStatus('upload', 'error'); } } showFileInfo(file, status) { const fileInfo = document.getElementById('file-info'); const fileName = document.getElementById('file-name'); const fileSize = document.getElementById('file-size'); const fileStatus = document.getElementById('file-status'); if (fileName) fileName.textContent = file.name; if (fileSize) fileSize.textContent = this.formatFileSize(file.size); if (fileStatus) fileStatus.textContent = status; if (fileInfo) { fileInfo.style.display = 'block'; fileInfo.classList.add('fade-in'); } } formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } // Processing Control enableProcessingControls() { const startBtn = document.getElementById('start-processing'); if (startBtn) startBtn.disabled = false; } async startProcessing() { if (!this.uploadedFile) { this.showAlert('错误', '请先上传STEP文件', 'warning'); return; } try { this.updateProcessingUI(true); this.updateStepStatus('process', 'active'); this.showProcessingSection(); const response = await fetch('/api/mesh/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ simulation_mode: false }) }); const result = await response.json(); if (response.ok) { this.startStatusPolling(); this.addLogEntry('info', '网格生成已启动'); } else { throw new Error(result.error || '启动失败'); } } catch (error) { console.error('Processing error:', error); this.showAlert('错误', `启动网格生成失败: ${error.message}`, 'danger'); this.updateProcessingUI(false); } } pauseProcessing() { this.showAlert('信息', '暂停功能暂未实现', 'info'); } stopProcessing() { if (this.statusPollingInterval) { clearInterval(this.statusPollingInterval); this.statusPollingInterval = null; } this.updateProcessingUI(false); this.addLogEntry('warning', '处理已停止'); } updateProcessingUI(isProcessing) { const startBtn = document.getElementById('start-processing'); const pauseBtn = document.getElementById('pause-processing'); const stopBtn = document.getElementById('stop-processing'); if (startBtn) startBtn.disabled = isProcessing; if (pauseBtn) pauseBtn.disabled = !isProcessing; if (stopBtn) stopBtn.disabled = !isProcessing; } showProcessingSection() { const processingSection = document.getElementById('processing-section'); if (processingSection) { processingSection.style.display = 'block'; processingSection.classList.add('fade-in'); } } // Status Polling startStatusPolling() { this.statusPollingInterval = setInterval(() => { this.checkProcessingStatus(); }, 2000); } async checkProcessingStatus() { try { const response = await fetch('/api/mesh/status'); const result = await response.json(); if (response.ok) { const statusData = result.status; this.updateStatusDisplay(statusData); if (statusData.status === 'COMPLETED') { this.handleProcessingComplete(); } else if (statusData.status === 'ERROR') { this.handleProcessingError(statusData.error_message); } } } catch (error) { console.error('Status polling error:', error); } } updateStatusDisplay(status) { const progressBar = document.getElementById('progress-bar'); const progressText = document.getElementById('progress-text'); const currentStep = document.getElementById('current-step'); const estimatedTime = document.getElementById('estimated-time'); const progress = status.progress_percentage || 0; if (progressBar) progressBar.style.width = `${progress}%`; if (progressText) progressText.textContent = `${progress}%`; if (currentStep) currentStep.textContent = status.current_operation || status.message || '处理中...'; if (estimatedTime) estimatedTime.textContent = status.estimated_time || '--'; if (status.message) { const logContainer = document.getElementById('log-container'); const lastLogEntry = logContainer?.lastElementChild; const newMessage = status.message; if (!lastLogEntry || !lastLogEntry.textContent.includes(newMessage)) { this.addLogEntry('info', newMessage); } } } handleProcessingComplete() { clearInterval(this.statusPollingInterval); this.statusPollingInterval = null; this.updateProcessingUI(false); this.updateStepStatus('process', 'completed'); this.updateStepStatus('quality', 'completed'); this.updateStepStatus('results', 'completed'); this.addLogEntry('success', '网格生成完成!'); this.showAlert('成功', '网格生成已完成!', 'success'); this.loadResults(); } handleProcessingError(error) { clearInterval(this.statusPollingInterval); this.statusPollingInterval = null; this.updateProcessingUI(false); this.addLogEntry('error', `处理失败: ${error}`); this.showAlert('错误', `网格生成失败: ${error}`, 'danger'); } addLogEntry(type, message) { const logContainer = document.getElementById('log-container'); if (!logContainer) return; const timestamp = new Date().toLocaleTimeString(); const logEntry = document.createElement('div'); logEntry.className = `log-entry ${type}`; logEntry.innerHTML = `${message}`; logContainer.appendChild(logEntry); logContainer.scrollTop = logContainer.scrollHeight; } // Results Display async loadResults() { try { const response = await fetch('/api/mesh/result'); const result = await response.json(); if (response.ok) { this.displayResults(result); this.showResultsSection(); } else { throw new Error(result.error || '获取结果失败'); } } catch (error) { console.error('Load results error:', error); this.showAlert('错误', `加载结果失败: ${error.message}`, 'danger'); } } displayResults(apiResult) { const result = apiResult.result; const basicInfo = result.basic_info; // Update statistics const elementCount = document.getElementById('element-count'); const nodeCount = document.getElementById('node-count'); const qualityScore = document.getElementById('quality-score'); const generationTime = document.getElementById('generation-time'); if (elementCount) elementCount.textContent = basicInfo.element_count?.toLocaleString() || '--'; if (nodeCount) nodeCount.textContent = basicInfo.node_count?.toLocaleString() || '--'; if (qualityScore) qualityScore.textContent = basicInfo.quality_score?.toFixed(2) || '--'; if (generationTime) generationTime.textContent = basicInfo.generation_time?.toFixed(1) || '--'; // Update quality status this.updateQualityStatus(basicInfo.quality_score); this.displayQualityDetails(result.quality_details); // Load visualization this.loadVisualization(); // Enable download buttons this.enableDownloadButtons(); } updateQualityStatus(score) { const qualityStatus = document.getElementById('quality-status'); if (!qualityStatus) return; const badge = qualityStatus.querySelector('.badge'); if (!badge) return; const normalizedScore = score / 100; if (normalizedScore >= 0.8) { badge.className = 'badge bg-success'; badge.textContent = '优秀'; } else if (normalizedScore >= 0.6) { badge.className = 'badge bg-warning'; badge.textContent = '良好'; } else if (normalizedScore >= 0.4) { badge.className = 'badge bg-warning'; badge.textContent = '一般'; } else { badge.className = 'badge bg-danger'; badge.textContent = '需要改进'; } } displayQualityDetails(details) { const qualityDetails = document.getElementById('quality-details'); if (!qualityDetails) return; const currentScore = document.getElementById('quality-score')?.textContent || '--'; qualityDetails.innerHTML = `
当前分数: ${currentScore}/100
状态: 网格质量检查通过
最高分: 100分
合格线: 40分
网格可视化图像暂不可用
网格已成功生成,但预览图像未找到网格可视化图像
`; }; img.onerror = () => { this.tryLoadImages(imageUrls, index + 1, container); }; img.src = imageUrls[index]; } findOrCreateVisualizationContainer() { let container = document.getElementById('mesh-visualization'); if (!container) { const resultsSection = document.getElementById('results-section'); if (!resultsSection) return null; const visualizationRow = document.createElement('div'); visualizationRow.className = 'row mt-4'; visualizationRow.innerHTML = `正在加载可视化图像...
生成时间: ${new Date().toLocaleString()}
系统: AnsysLink - 叶片网格生成助手
| 项目 | 值 |
|---|---|
| 文件名 | ${fileInfo.filename || 'blade.step'} |
| 文件大小 | ${fileInfo.file_size_mb ? fileInfo.file_size_mb + ' MB' : this.formatFileSize(fileInfo.file_size || 0)} |
| 上传时间 | ${fileInfo.upload_time ? new Date(fileInfo.upload_time).toLocaleString() : '未知'} |
| 评估项目 | 结果 | 状态 |
|---|---|---|
| 总体质量状态 | ${basicInfo.quality_status || '通过'} | ${this.getQualityLabel(basicInfo.quality_score)} |
| 质量分数 | ${(basicInfo.quality_score || 0).toFixed(2)} / 100 | ${basicInfo.quality_score >= 80 ? '优秀' : basicInfo.quality_score >= 60 ? '良好' : basicInfo.quality_score >= 40 ? '及格' : '需改进'} |
| 最小单元质量 | ${basicInfo.min_element_quality || '计算中'} | - |
| 项目 | 信息 |
|---|---|
| 处理状态 | ${processingInfo.status || '已完成'} |
| 开始时间 | ${processingInfo.started_at ? new Date(processingInfo.started_at).toLocaleString() : '未知'} |
| 完成时间 | ${processingInfo.completed_at ? new Date(processingInfo.completed_at).toLocaleString() : new Date().toLocaleString()} |
| 总处理时间 | ${(processingInfo.total_time || 0).toFixed(1)} 秒 |
| 项目 | 信息 |
|---|---|
| 软件版本 | AnsysLink v1.0 |
| ANSYS版本 | 2024 R1 (仿真模式) |
| 处理模式 | 自动网格生成 |
| 报告生成 | ${new Date().toLocaleString()} |
注意: 本报告由AnsysLink自动生成。所有数据基于网格生成过程中的实时计算结果。
如需更详细的分析报告,请联系技术支持团队。