// 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 = `[${timestamp}]${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分

质量评估基于单元形状、长宽比、偏斜度等多项指标综合计算
`; } showResultsSection() { const resultsSection = document.getElementById('results-section'); if (resultsSection) { resultsSection.style.display = 'block'; resultsSection.classList.add('fade-in'); } } loadVisualization() { const visualizationContainer = this.findOrCreateVisualizationContainer(); fetch('/api/mesh/result?include_visualization=true') .then(response => response.json()) .then(result => { if (result.success && result.result.basic_info.mesh_image_path) { this.displayMeshImage(result.result.basic_info.mesh_image_path); } else { this.loadStaticVisualization(); } }) .catch(error => { console.log('可视化加载失败:', error); this.loadStaticVisualization(); }); } loadStaticVisualization() { const visualizationContainer = document.getElementById('mesh-visualization'); if (!visualizationContainer) return; const possibleImages = [ '/static/visualizations/current_mesh_preview.png', '/static/visualizations/mesh_visualization_' + new Date().toISOString().slice(0,10).replace(/-/g,'') + '.png' ]; this.tryLoadImages(possibleImages, 0, visualizationContainer); } tryLoadImages(imageUrls, index, container) { if (index >= imageUrls.length) { container.innerHTML = `

网格可视化图像暂不可用

网格已成功生成,但预览图像未找到
`; return; } const img = new Image(); img.onload = () => { container.innerHTML = ` 网格可视化

网格可视化图像

`; }; 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 = `
网格可视化
加载中...

正在加载可视化图像...

`; const exportRow = resultsSection.querySelector('.row.mt-4'); if (exportRow) { exportRow.parentNode.insertBefore(visualizationRow, exportRow); } else { resultsSection.appendChild(visualizationRow); } container = document.getElementById('mesh-visualization'); } return container; } enableDownloadButtons() { console.log('Enabling download buttons...'); const downloadMesh = document.getElementById('download-mesh'); const downloadReport = document.getElementById('download-report'); const downloadImage = document.getElementById('download-image'); console.log('Found buttons:', { mesh: !!downloadMesh, report: !!downloadReport, image: !!downloadImage }); if (downloadMesh) { downloadMesh.disabled = false; downloadMesh.classList.remove('disabled'); console.log('Mesh download button enabled'); } else { console.error('Mesh download button not found!'); } if (downloadReport) { downloadReport.disabled = false; downloadReport.classList.remove('disabled'); console.log('Report download button enabled'); } else { console.error('Report download button not found!'); } if (downloadImage) { downloadImage.disabled = false; downloadImage.classList.remove('disabled'); console.log('Image download button enabled'); } else { console.error('Image download button not found!'); } console.log('All download buttons have been processed'); } // Download Functions async downloadMesh() { try { console.log('Starting mesh file download...'); this.showAlert('信息', '正在准备网格文件下载...', 'info'); const response = await fetch('/api/mesh/download/mesh'); console.log('Download response status:', response.status); if (response.ok) { const blob = await response.blob(); console.log('Downloaded blob size:', blob.size); if (blob.size === 0) { throw new Error('下载的文件为空'); } const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `blade_mesh_${new Date().toISOString().slice(0,10).replace(/-/g,'')}.mechdb`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); this.showAlert('成功', '网格文件下载已开始', 'success'); } else { const errorData = await response.json().catch(() => ({error: '下载失败'})); throw new Error(errorData.error || `HTTP ${response.status}`); } } catch (error) { console.error('Download mesh error:', error); this.showAlert('错误', '网格文件下载失败:' + error.message, 'danger'); } } async downloadReport() { try { console.log('Starting report download...'); this.showAlert('信息', '正在生成质量报告...', 'info'); const response = await fetch('/api/mesh/result?include_quality_details=true&format=summary'); console.log('Report response status:', response.status); if (response.ok) { const result = await response.json(); console.log('Report data received:', result); this.generatePDFReport(result); } else { const errorData = await response.json().catch(() => ({error: '获取报告数据失败'})); throw new Error(errorData.error || `HTTP ${response.status}`); } } catch (error) { console.error('Download report error:', error); this.showAlert('错误', '质量报告下载失败:' + error.message, 'danger'); } } async downloadImage() { try { console.log('Starting image download...'); this.showAlert('信息', '正在准备图像下载...', 'info'); const response = await fetch('/api/mesh/download/image'); console.log('Image download response status:', response.status); if (response.ok) { const blob = await response.blob(); console.log('Downloaded image blob size:', blob.size); console.log('Downloaded image blob type:', blob.type); if (blob.size === 0) { throw new Error('下载的图像文件为空'); } const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `mesh_visualization_${new Date().toISOString().slice(0,10).replace(/-/g,'')}.png`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); this.showAlert('成功', '可视化图像下载已开始', 'success'); } else { const errorData = await response.json().catch(() => ({error: '图像下载失败'})); throw new Error(errorData.error || `HTTP ${response.status}`); } } catch (error) { console.error('Download image error:', error); this.showAlert('错误', '图像下载失败:' + error.message, 'danger'); } } generatePDFReport(data) { // Get more complete data from the API response const result = data.result || data.summary || {}; const basicInfo = result.basic_info || result.mesh_statistics || {}; const fileInfo = result.file_info || {}; const processingInfo = result.processing_info || result.processing_summary || {}; const reportContent = ` 网格质量报告

CAE网格生成质量报告

生成时间: ${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.element_count || basicInfo.elements || 0).toLocaleString()}
单元数量
${(basicInfo.node_count || basicInfo.nodes || 0).toLocaleString()}
节点数量
${(basicInfo.quality_score || 0).toFixed(2)}
质量评分 (0-100)
${(basicInfo.generation_time || processingInfo.total_time || 0).toFixed(1)}s
生成时间

✅ 质量评估

评估项目结果状态
总体质量状态 ${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自动生成。所有数据基于网格生成过程中的实时计算结果。

如需更详细的分析报告,请联系技术支持团队。

`; const blob = new Blob([reportContent], { type: 'text/html' }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `mesh_quality_report_${new Date().toISOString().slice(0,10)}.html`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); this.showAlert('成功', '质量报告已下载为HTML文件,可在浏览器中打开查看或打印为PDF', 'success'); } // Helper function for quality recommendations generateQualityRecommendations(qualityScore) { const recommendations = []; const score = qualityScore || 0; if (score >= 80) { recommendations.push('网格质量优秀,可以进行高精度分析'); recommendations.push('网格密度适中,计算效率良好'); recommendations.push('所有质量指标均达到推荐标准'); } else if (score >= 60) { recommendations.push('网格质量良好,适合大多数分析需求'); recommendations.push('考虑在高应力区域增加局部细化'); recommendations.push('整体网格分布合理,可满足工程精度要求'); } else if (score >= 40) { recommendations.push('网格质量达到基本要求,建议进一步优化'); recommendations.push('考虑减小全局单元尺寸以提高质量'); recommendations.push('检查高曲率区域的网格细化程度'); } else { recommendations.push('网格质量需要改进,建议重新生成'); recommendations.push('考虑调整网格参数和细化策略'); recommendations.push('建议联系技术支持获得优化建议'); } // Add general recommendations recommendations.push('定期检查网格质量以确保分析准确性'); recommendations.push('保存当前网格设置以便后续重用'); return recommendations; } // Helper function for quality class getQualityClass(score) { if (!score) return ''; if (score >= 80) return 'quality-good'; if (score >= 60) return 'quality-warning'; return 'quality-danger'; } // Helper function for quality label getQualityLabel(score) { if (!score) return '未知'; if (score >= 80) return '优秀'; if (score >= 60) return '良好'; if (score >= 40) return '及格'; return '需改进'; } // Visualization Controls changeView(viewType) { this.addLogEntry('info', `切换到${viewType}视图`); } refreshVisualization() { this.addLogEntry('info', '刷新可视化'); this.loadVisualization(); } // Step Status Management updateStepStatus(stepId, status) { const stepElement = document.getElementById(`step-${stepId}`); if (stepElement) { stepElement.classList.remove('active', 'completed', 'error'); stepElement.classList.add(status); } } // System Status Check async checkSystemStatus() { try { const ansysStatus = document.getElementById('ansys-status'); const queueStatus = document.getElementById('queue-status'); if (ansysStatus) { ansysStatus.innerHTML = ' 正常'; } if (queueStatus) { queueStatus.textContent = '0 个任务'; } } catch (error) { console.error('System status check error:', error); const ansysStatus = document.getElementById('ansys-status'); if (ansysStatus) { ansysStatus.innerHTML = ' 连接失败'; } } } // Alert System showAlert(title, message, type = 'info') { let alertContainer = document.getElementById('alert-container'); if (!alertContainer) { alertContainer = document.createElement('div'); alertContainer.id = 'alert-container'; alertContainer.style.position = 'fixed'; alertContainer.style.top = '20px'; alertContainer.style.right = '20px'; alertContainer.style.zIndex = '9999'; alertContainer.style.maxWidth = '400px'; document.body.appendChild(alertContainer); } const alert = document.createElement('div'); alert.className = `alert alert-${type} alert-dismissible fade show mb-2`; alert.innerHTML = ` ${title}: ${message} `; alertContainer.appendChild(alert); // Auto-dismiss after 5 seconds setTimeout(() => { if (alert.parentNode) { alert.remove(); } }, 5000); } } // Initialize the application when DOM is loaded document.addEventListener('DOMContentLoaded', () => { new MeshGeneratorApp(); });