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;
}
}
}