using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using Autodesk.Revit.DB; using RevitHttpControl.Models; namespace RevitHttpControl.Services { /// /// 薄壳优化执行服务 /// public class ShellOptimizer { private readonly ShellAnalyzer _analyzer; public ShellOptimizer() { _analyzer = new ShellAnalyzer(); } /// /// 执行薄壳优化 /// public ShellOptimizeResult ExecuteOptimization(Document doc, ShellOptimizeMode mode, bool backupOriginal = true) { if (doc == null) throw new ArgumentNullException(nameof(doc)); var stopwatch = Stopwatch.StartNew(); var result = new ShellOptimizeResult(); try { // 1. 获取原始文件大小 var originalSize = GetDocumentFileSize(doc); result.OriginalSize = FormatFileSize(originalSize); // 2. 分析模型获取删除列表 var analysis = _analyzer.AnalyzeModel(doc, mode); var elementsToDelete = GetElementsToDelete(doc, analysis); // 3. 创建备份(如果需要) if (backupOriginal) { result.BackupPath = CreateBackup(doc); } // 4. 执行删除操作 var deletedCount = DeleteElements(doc, elementsToDelete); result.RemovedCount = deletedCount; // 5. 保存文档并计算优化后大小 SaveDocument(doc); var optimizedSize = GetDocumentFileSize(doc); result.OptimizedSize = FormatFileSize(optimizedSize); // 6. 计算减少百分比 if (originalSize > 0) { var reduction = ((double)(originalSize - optimizedSize) / originalSize) * 100; result.Reduction = $"{reduction:F1}%"; } else { result.Reduction = "0%"; } stopwatch.Stop(); result.ProcessingTimeSeconds = stopwatch.Elapsed.TotalSeconds; return result; } catch (Exception ex) { stopwatch.Stop(); result.ProcessingTimeSeconds = stopwatch.Elapsed.TotalSeconds; // 如果有备份且操作失败,可以考虑恢复备份 throw new InvalidOperationException($"薄壳优化执行失败: {ex.Message}", ex); } } /// /// 根据分析结果获取需要删除的构件 /// private List GetElementsToDelete(Document doc, ShellAnalyzeResponse analysis) { var elementsToDelete = new List(); // 重新分析获取详细的构件列表(为了安全性) var analyzer = new ShellAnalyzer(); var detailedAnalysis = analyzer.AnalyzeModel(doc, ShellOptimizeMode.Standard); // 获取所有需要删除的构件 var allElements = new FilteredElementCollector(doc) .WhereElementIsNotElementType() .WhereElementIsViewIndependent() .ToElements(); foreach (var element in allElements) { if (ShouldDeleteElement(element, analysis)) { elementsToDelete.Add(element.Id); } } return elementsToDelete; } /// /// 判断构件是否应该删除 /// private bool ShouldDeleteElement(Element element, ShellAnalyzeResponse analysis) { if (element.Category == null) return false; var categoryName = element.Category.Name; var categoryStats = analysis.Categories.FirstOrDefault(c => c.Name == categoryName); // 如果该类别没有删除项,则不删除 if (categoryStats == null || categoryStats.Remove == 0) return false; // 这里可以实现更复杂的逻辑,比如优先删除某些构件 // 目前简化为按类别统计比例删除 return true; } /// /// 批量删除构件 /// private int DeleteElements(Document doc, List elementIds) { if (elementIds == null || elementIds.Count == 0) return 0; int deletedCount = 0; const int batchSize = 100; // 批量处理大小 // 分批删除以提高性能和稳定性 for (int i = 0; i < elementIds.Count; i += batchSize) { var batch = elementIds.Skip(i).Take(batchSize).ToList(); deletedCount += DeleteElementBatch(doc, batch); } return deletedCount; } /// /// 删除一批构件 /// private int DeleteElementBatch(Document doc, List elementIds) { using (var transaction = new Transaction(doc, "薄壳优化删除构件")) { transaction.Start(); try { // 过滤出可以删除的构件 var deletableIds = new List(); foreach (var id in elementIds) { var element = doc.GetElement(id); if (element != null && CanDeleteElement(element)) { deletableIds.Add(id); } } // 执行删除 if (deletableIds.Count > 0) { var deletedIds = doc.Delete(deletableIds); transaction.Commit(); return deletedIds.Count; } else { transaction.RollBack(); return 0; } } catch (Exception ex) { transaction.RollBack(); // 记录错误但继续处理其他构件 System.Diagnostics.Debug.WriteLine($"删除构件批次失败: {ex.Message}"); return 0; } } } /// /// 检查构件是否可以安全删除 /// private bool CanDeleteElement(Element element) { try { // 检查构件是否被锁定 if (element.Pinned) return false; // 检查构件是否在工作集中被其他用户编辑 var worksetId = element.WorksetId; if (worksetId != WorksetId.InvalidWorksetId) { var doc = element.Document; var worksetTable = doc.GetWorksetTable(); var workset = worksetTable.GetWorkset(worksetId); if (workset.IsOpen && workset.Owner != doc.Application.Username) return false; } // 检查是否有依赖关系(如主体构件) if (HasCriticalDependencies(element)) return false; return true; } catch { // 如果检查过程出错,为安全起见不删除 return false; } } /// /// 检查构件是否有关键依赖关系 /// private bool HasCriticalDependencies(Element element) { try { // 对于墙体,检查是否有门窗等构件托管在上面 if (element is Wall wall) { var doc = wall.Document; var collector = new FilteredElementCollector(doc); var familyInstances = collector.OfClass(typeof(FamilyInstance)).ToElements(); // 检查是否有门窗托管在这面墙上 foreach (Element e in familyInstances) { var familyInstance = e as FamilyInstance; if (familyInstance?.Host?.Id == wall.Id) { return true; // 有构件托管在这面墙上,不能删除 } } } return false; } catch { return true; // 无法确定时,为安全起见认为有依赖 } } /// /// 创建文件备份 /// private string CreateBackup(Document doc) { try { var originalPath = doc.PathName; if (string.IsNullOrEmpty(originalPath)) { // 如果文档未保存,先保存 throw new InvalidOperationException("文档必须先保存才能创建备份"); } var directory = Path.GetDirectoryName(originalPath); var fileName = Path.GetFileNameWithoutExtension(originalPath); var extension = Path.GetExtension(originalPath); var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); var backupFileName = $"{fileName}_backup_{timestamp}{extension}"; var backupPath = Path.Combine(directory, backupFileName); // 复制文件 File.Copy(originalPath, backupPath, false); return backupPath; } catch (Exception ex) { throw new InvalidOperationException($"创建备份失败: {ex.Message}", ex); } } /// /// 保存文档 /// private void SaveDocument(Document doc) { try { if (doc.IsModified) { doc.Save(); } } catch (Exception ex) { throw new InvalidOperationException($"保存文档失败: {ex.Message}", ex); } } /// /// 获取文档文件大小 /// private long GetDocumentFileSize(Document doc) { try { var filePath = doc.PathName; if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) return 0; var fileInfo = new FileInfo(filePath); return fileInfo.Length; } catch { return 0; } } /// /// 格式化文件大小显示 /// private string FormatFileSize(long bytes) { if (bytes == 0) return "0 字节"; string[] sizes = { "字节", "KB", "MB", "GB", "TB" }; int order = 0; double size = bytes; while (size >= 1024 && order < sizes.Length - 1) { order++; size /= 1024; } return $"{size:F2} {sizes[order]}"; } } }