using System; using System.Collections.Generic; using System.IO; using System.Linq; using Autodesk.Revit.DB; using RevitHttpControl.Models; using RevitHttpControl.Common; using ExportRange = RevitHttpControl.Models.ExportRange; namespace RevitHttpControl.Services { /// /// 导出服务配置常量 /// internal static class ExportConstants { /// /// 同步导出超时时间(分钟) /// public const int SYNC_EXPORT_TIMEOUT_MINUTES = 5; /// /// 轮询间隔(毫秒) /// public const int POLLING_INTERVAL_MS = 200; /// /// 默认IFC文件扩展名 /// public const string IFC_FILE_EXTENSION = ".ifc"; /// /// 临时文件前缀 /// public const string TEMP_FILE_PREFIX = "revit_export_test_"; } /// /// 导出服务类 /// public static class ExportService { /// /// 同步导出IFC文件 /// /// 导出请求参数 /// 导出结果 public static ExportIfcResponse ExportToIfc(ExportIfcRequest request) { if (request == null) throw new ArgumentNullException(nameof(request)); var startTime = DateTime.Now; ExportIfcResponse result = null; Exception capturedException = null; var completed = false; try { App.Instance.EnqueueCommand(uiApp => { try { result = ExecuteIfcExportCore(request, uiApp, startTime); } catch (Exception ex) { capturedException = ex; } finally { completed = true; } }); // 等待命令执行完成 var timeout = DateTime.Now.AddMinutes(ExportConstants.SYNC_EXPORT_TIMEOUT_MINUTES); while (!completed && DateTime.Now < timeout) { System.Threading.Thread.Sleep(ExportConstants.POLLING_INTERVAL_MS); } if (capturedException != null) throw capturedException; if (!completed) throw new TimeoutException(ErrorMessages.OPERATION_TIMEOUT_MSG); return result; } catch (Exception ex) { throw new InvalidOperationException($"{ErrorMessages.IFC_EXPORT_FAILED_MSG}: {ex.Message}", ex); } } /// /// 异步导出IFC文件 /// /// 导出请求参数 /// 任务ID /// 任务管理器 public static void ExportToIfcAsync(ExportIfcRequest request, string taskId, TaskManager taskManager) { if (request == null) throw new ArgumentNullException(nameof(request)); var startTime = DateTime.Now; App.Instance.EnqueueCommand(uiApp => { try { var result = ExecuteIfcExportCore(request, uiApp, startTime); taskManager.CompleteTask(new Guid(taskId), result); } catch (Exception ex) { taskManager.FailTask(new Guid(taskId), $"{ErrorMessages.IFC_EXPORT_FAILED_MSG}: {ex.Message}"); } }); } /// /// 核心IFC导出逻辑(同步和异步共用) /// /// 导出请求参数 /// Revit UI应用程序 /// 开始时间 /// 导出结果 private static ExportIfcResponse ExecuteIfcExportCore(ExportIfcRequest request, Autodesk.Revit.UI.UIApplication uiApp, DateTime startTime) { var doc = uiApp.ActiveUIDocument?.Document; if (doc == null) throw new InvalidOperationException(ErrorMessages.NO_DOCUMENT_OPEN_MSG); // 生成输出路径 var outputPath = GenerateOutputPath(request, doc); // 验证输出路径 ValidateOutputPath(outputPath); // 配置IFC导出选项 var ifcExportOptions = ConfigureIfcExportOptions(request); // 执行IFC导出 var exportResult = ExecuteIfcExport(doc, outputPath, ifcExportOptions); // 生成响应 return CreateExportResponse(outputPath, exportResult, startTime, request); } /// /// 获取支持的导出格式 /// /// 支持的格式列表 public static List GetSupportedFormats() { return new List { new ExportFormat { Format = "IFC", Name = "Industry Foundation Classes", Description = "建筑信息模型标准交换格式", Extensions = new List { ".ifc" }, SupportsAsync = true, IsExperimental = false } }; } /// /// 生成输出文件路径 /// /// 导出请求 /// Revit文档 /// 输出文件路径 private static string GenerateOutputPath(ExportIfcRequest request, Document doc) { if (!string.IsNullOrWhiteSpace(request.OutputPath)) { // 确保路径有正确的扩展名 if (!request.OutputPath.EndsWith(".ifc", StringComparison.OrdinalIgnoreCase)) { request.OutputPath += ".ifc"; } return request.OutputPath; } // 自动生成路径 var docPath = doc.PathName; var directory = string.IsNullOrEmpty(docPath) ? Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) : Path.GetDirectoryName(docPath); var fileName = string.IsNullOrEmpty(docPath) ? doc.Title : Path.GetFileNameWithoutExtension(docPath); var prefix = string.IsNullOrWhiteSpace(request.FileNamePrefix) ? "Export" : request.FileNamePrefix; var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); var outputFileName = $"{prefix}_{fileName}_{timestamp}.ifc"; return Path.Combine(directory, outputFileName); } /// /// 验证输出路径 /// /// 输出路径 private static void ValidateOutputPath(string outputPath) { try { // 基本路径验证 if (string.IsNullOrWhiteSpace(outputPath)) throw new ArgumentException(ErrorMessages.EXPORT_PATH_INVALID_MSG); // 检查路径安全性(防止路径遍历攻击) var fullPath = Path.GetFullPath(outputPath); if (!fullPath.Equals(outputPath, StringComparison.OrdinalIgnoreCase)) { // 如果规范化后的路径与原路径不同,可能存在路径遍历 throw new ArgumentException(ErrorMessages.EXPORT_PATH_INVALID_MSG); } // 检查文件扩展名 var extension = Path.GetExtension(outputPath); if (!extension.Equals(ExportConstants.IFC_FILE_EXTENSION, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException($"输出文件必须是{ExportConstants.IFC_FILE_EXTENSION}格式"); } var directory = Path.GetDirectoryName(outputPath); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } // 检查写入权限(使用更安全的方式) var testFile = Path.Combine(directory, $"{ExportConstants.TEMP_FILE_PREFIX}{Guid.NewGuid()}.tmp"); File.WriteAllText(testFile, "test"); File.Delete(testFile); } catch (UnauthorizedAccessException) { throw new InvalidOperationException(ErrorMessages.EXPORT_PATH_ACCESS_DENIED_MSG); } catch (DirectoryNotFoundException) { throw new InvalidOperationException(ErrorMessages.EXPORT_PATH_INVALID_MSG); } catch (ArgumentException) { throw; // 重新抛出参数异常 } catch (Exception ex) { throw new InvalidOperationException($"{ErrorMessages.EXPORT_PATH_INVALID_MSG}: {ex.Message}"); } } /// /// 配置IFC导出选项 /// /// 导出请求 /// IFC导出选项 private static IFCExportOptions ConfigureIfcExportOptions(ExportIfcRequest request) { var options = new IFCExportOptions(); // 设置IFC版本 switch (request.Version) { case IfcVersion.IFC2x3: options.FileVersion = IFCVersion.IFC2x3CV2; break; case IfcVersion.IFC4: options.FileVersion = IFCVersion.IFC4; break; case IfcVersion.IFC4RV: options.FileVersion = IFCVersion.IFC4RV; break; case IfcVersion.IFC4DTV: options.FileVersion = IFCVersion.IFC4DTV; break; default: options.FileVersion = IFCVersion.IFC2x3CV2; break; } // 设置导出范围 switch (request.Range) { case ExportRange.VisibleElements: options.FilterViewId = ElementId.InvalidElementId; break; case ExportRange.CurrentView: // 当前视图导出需要在调用时设置 break; case ExportRange.EntireProject: options.FilterViewId = ElementId.InvalidElementId; break; } // 其他选项 options.SpaceBoundaryLevel = request.IncludeSpaceBoundaries ? 2 : 0; // 注意:Revit 2017的IFCExportOptions可能不支持某些属性 // options.SplitWallsAndColumns = request.SplitWallsAndColumns; // Revit 2017可能不支持 // options.Export2DElements = request.Include2DElements; // Revit 2017可能不支持 return options; } /// /// 执行IFC导出 /// /// Revit文档 /// 输出路径 /// 导出选项 /// 导出结果 private static bool ExecuteIfcExport(Document doc, string outputPath, IFCExportOptions options) { try { using (var transaction = new Transaction(doc, "IFC Export")) { transaction.Start(); // 执行IFC导出 - Revit 2017 API var result = doc.Export(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath), options); transaction.Commit(); return result; } } catch (Exception ex) { throw new InvalidOperationException($"{ErrorMessages.IFC_EXPORT_FAILED_MSG}: {ex.Message}", ex); } } /// /// 创建导出响应 /// /// 输出路径 /// 导出是否成功 /// 开始时间 /// 原始请求 /// 导出响应 private static ExportIfcResponse CreateExportResponse(string outputPath, bool exportSuccess, DateTime startTime, ExportIfcRequest request) { if (!exportSuccess || !File.Exists(outputPath)) { throw new InvalidOperationException(ErrorMessages.IFC_EXPORT_FAILED_MSG); } var fileInfo = new FileInfo(outputPath); var processingTime = (DateTime.Now - startTime).TotalSeconds; var response = new ExportIfcResponse { FilePath = outputPath, FileName = fileInfo.Name, FileSizeBytes = fileInfo.Length, FileSizeDisplay = FormatFileSize(fileInfo.Length), ProcessingTimeSeconds = Math.Round(processingTime, 2), IfcVersion = request.Version.ToString(), ExportedAt = DateTime.Now, Statistics = new ExportStatistics { HasGeometry = true, HasProperties = true } }; // 尝试获取导出统计信息 try { response.ExportedElementsCount = GetExportedElementsCount(outputPath); response.Statistics = GetExportStatistics(outputPath, request); } catch (Exception ex) { // 统计信息获取失败不影响主要功能 System.Diagnostics.Debug.WriteLine($"Failed to get export statistics: {ex.Message}"); response.ExportedElementsCount = 0; } return response; } /// /// 格式化文件大小显示 /// /// 字节数 /// 格式化的文件大小 private static string FormatFileSize(long bytes) { string[] sizes = { "B", "KB", "MB", "GB" }; double len = bytes; int order = 0; while (len >= 1024 && order < sizes.Length - 1) { order++; len = len / 1024; } return $"{len:0.##} {sizes[order]}"; } /// /// 获取导出的元素数量(简化实现) /// /// IFC文件路径 /// 元素数量 private static int GetExportedElementsCount(string ifcFilePath) { try { // 简单的行数统计作为元素数量的近似值 var lines = File.ReadAllLines(ifcFilePath); return lines.Count(line => line.StartsWith("#") && line.Contains("=")); } catch { return 0; } } /// /// 获取导出统计信息(简化实现) /// /// IFC文件路径 /// 原始请求 /// 统计信息 private static ExportStatistics GetExportStatistics(string ifcFilePath, ExportIfcRequest request) { var statistics = new ExportStatistics { HasGeometry = true, HasProperties = true, ElementsByCategory = new Dictionary(), ElementsByLevel = new Dictionary() }; try { // 这里可以添加更详细的IFC文件分析 // 目前提供基本的统计信息 statistics.CategoriesCount = request.Categories?.Count ?? 0; statistics.LevelsCount = request.LevelNames?.Count ?? 0; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Failed to analyze IFC file: {ex.Message}"); } return statistics; } } }