diff --git a/NavisworksTransport.UnitTests.csproj b/NavisworksTransport.UnitTests.csproj
index ab35c52..7068f09 100644
--- a/NavisworksTransport.UnitTests.csproj
+++ b/NavisworksTransport.UnitTests.csproj
@@ -84,6 +84,7 @@
+
diff --git a/UnitTests/Integration/NavisworksTestAutomationClient.cs b/UnitTests/Integration/NavisworksTestAutomationClient.cs
index fe813c2..ef1392a 100644
--- a/UnitTests/Integration/NavisworksTestAutomationClient.cs
+++ b/UnitTests/Integration/NavisworksTestAutomationClient.cs
@@ -293,6 +293,23 @@ namespace NavisworksTransport.UnitTests.Integration
return await PostJsonAsync(requestUri, 60).ConfigureAwait(false);
}
+ ///
+ /// 剖面盒导出(指定包围盒模式),返回导出文件信息。
+ ///
+ public async Task ExportSectionBoxAsync(
+ string filePath,
+ double x1, double y1, double z1,
+ double x2, double y2, double z2)
+ {
+ string requestUri = string.Format(
+ CultureInfo.InvariantCulture,
+ "/api/test/export-section-box?mode=bbox&x1={0}&y1={1}&z1={2}&x2={3}&y2={4}&z2={5}&path={6}",
+ x1, y1, z1, x2, y2, z2,
+ Uri.EscapeDataString(filePath));
+
+ return await PostJsonAsync(requestUri, 120).ConfigureAwait(false);
+ }
+
private async Task GetJsonAsync(string requestUri, int timeoutSeconds = 30)
{
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)))
diff --git a/UnitTests/Integration/SectionBoxExportAutomationTests.cs b/UnitTests/Integration/SectionBoxExportAutomationTests.cs
new file mode 100644
index 0000000..3401c8c
--- /dev/null
+++ b/UnitTests/Integration/SectionBoxExportAutomationTests.cs
@@ -0,0 +1,83 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Newtonsoft.Json.Linq;
+
+namespace NavisworksTransport.UnitTests.Integration
+{
+ ///
+ /// 剖面盒导出集成测试(覆盖计划 T16)。
+ ///
+ /// 验证 SectionBoxExporter 的指定包围盒导出链路:
+ /// - 导出 NWD 文件成功(文件存在且非空)
+ /// - 包围盒内对象数 > 0、文件大小 > 0
+ /// - 隐藏节点计算完成(hiddenNodeCount >= 0)
+ ///
+ /// 使用指定包围盒模式(bbox),不依赖 UI 剖面盒状态;包围盒覆盖模型 Hoisting/Rail 区域。
+ ///
+ [TestClass]
+ [TestCategory("NavisworksIntegration")]
+ public class SectionBoxExportAutomationTests
+ {
+ // 模型单位:覆盖 Hoisting/Rail 区域的包围盒(已验证含 57 个对象)
+ private const double MinX = -240, MinY = 10, MinZ = -20;
+ private const double MaxX = -170, MaxY = 30, MaxZ = 25;
+
+ [TestMethod]
+ [Timeout(240000)]
+ public async Task SectionBoxBBoxExport_ProducesValidNwd()
+ {
+ using (var client = new NavisworksTestAutomationClient())
+ {
+ await client.EnsureServiceReadyAsync(TimeSpan.FromSeconds(90)).ConfigureAwait(false);
+
+ string tempDir = Path.Combine(Path.GetTempPath(), "NavisworksTransportIntegrationTests", Guid.NewGuid().ToString("N"));
+ Directory.CreateDirectory(tempDir);
+ try
+ {
+ string exportPath = Path.Combine(tempDir, "sectionbox-export.nwd");
+
+ JObject response = await client.ExportSectionBoxAsync(
+ exportPath,
+ MinX, MinY, MinZ, MaxX, MaxY, MaxZ).ConfigureAwait(false);
+ Assert.IsTrue(response.Value("ok"), "剖面盒导出失败: " + (string)response["error"]);
+
+ JObject data = (JObject)response["data"];
+
+ // 文件已生成且非空
+ Assert.IsFalse(string.IsNullOrWhiteSpace((string)data["filePath"]), "导出文件路径为空");
+ Assert.IsTrue(File.Exists(exportPath), "导出文件不存在: " + exportPath);
+ Assert.IsTrue((long)data["fileSize"] > 0, "导出文件大小应大于 0");
+ Assert.IsTrue(new FileInfo(exportPath).Length > 0, "导出文件实际为空");
+
+ // 包围盒内对象与隐藏节点
+ Assert.IsTrue((int)data["objectCount"] > 0, "包围盒内应找到对象");
+ Assert.IsTrue((int)data["hiddenNodeCount"] >= 0, "隐藏节点数不能为负");
+
+ Assert.AreEqual("bbox", (string)data["mode"], "导出模式应为 bbox");
+ }
+ finally
+ {
+ SafeDelete(tempDir);
+ }
+ }
+ }
+
+ private static void SafeDelete(string directory)
+ {
+ if (!Directory.Exists(directory))
+ {
+ return;
+ }
+
+ try
+ {
+ Directory.Delete(directory, true);
+ }
+ catch
+ {
+ }
+ }
+ }
+}
diff --git a/doc/working/2026-08-02-integration-test-coverage-plan.md b/doc/working/2026-08-02-integration-test-coverage-plan.md
index f5b2231..f7ccfc5 100644
--- a/doc/working/2026-08-02-integration-test-coverage-plan.md
+++ b/doc/working/2026-08-02-integration-test-coverage-plan.md
@@ -54,6 +54,7 @@
- [x] **T13 动画播放控制**(2026-08-04,`AnimationPlaybackControlAutomationTests`)— prepare→start→pause 验证→resume→finished
- [x] **T14 批量任务队列**(2026-08-04,`BatchQueueAutomationTests`)— 队列添加/计数递增(轻量版,不执行批量动画)
- [x] **T15 路径分析**(2026-08-04,`PathAnalysisAutomationTests`)— 评分结构验证;顺带修复产品 bug(CollisionResults 表已废弃导致路径分析 no such table)
+- [x] **T16 剖面盒导出**(2026-08-04,`SectionBoxExportAutomationTests`)— 指定包围盒模式导出 NWD(对象数/文件大小/隐藏节点),不依赖 UI 剖面盒状态
## 4. 测试服务支撑能力
@@ -74,6 +75,7 @@
| animation-playback-control | ✅ 已实现 | 播放控制序列(T13) |
| batch-queue-add | ✅ 已实现 | 批量队列添加(T14) |
| analyze-path | ✅ 已实现 | 路径分析评分(T15) |
+| export-section-box | ✅ 已实现 | 剖面盒导出 NWD(bbox/activeView 模式,T16) |
| 其余待扩展 | — | 批量执行、TimeLiner 集成等后续迭代补充 |
## 5. 完成记录
@@ -88,3 +90,4 @@
| 2026-08-04 | `44214ba` | T05/T06 + 基础修复(Free 路径、报告断言、动态解析、串行锁、脚本、加速 15fps/5s) |
| 2026-08-04 | 待提交 | T07-T10(障碍绕行、多策略、网格诊断、持久化)+ GetDetectionRecordById + detection-record 端点;P0+P1 完成,18/18 |
| 2026-08-04 | 待提交 | T11-T15(路径编辑/导出/播放控制/批量队列/路径分析)+ 服务端 5 端点 + 产品 bug 修复(PathDatabase CollisionResults→CollisionReports、RemovePoint 链路);P0+P1+P2 全部完成,集成测试 24/24,完整集 74.6s |
+| 2026-08-04 | 待提交 | T16 剖面盒导出(SectionBoxExportAutomationTests + export-section-box 端点,bbox 模式),25/25 |
diff --git a/src/Core/Services/TestAutomationHttpService.cs b/src/Core/Services/TestAutomationHttpService.cs
index 6bed637..1571215 100644
--- a/src/Core/Services/TestAutomationHttpService.cs
+++ b/src/Core/Services/TestAutomationHttpService.cs
@@ -298,6 +298,14 @@ namespace NavisworksTransport.Core.Services
return;
}
+ if (string.Equals(method, "POST", StringComparison.OrdinalIgnoreCase) &&
+ string.Equals(request.Path, "/api/test/export-section-box", StringComparison.OrdinalIgnoreCase))
+ {
+ object payload = InvokeOnUiThread(() => ExportSectionBoxPayload(request.Query));
+ await WriteJsonResponseAsync(writer, 200, BuildEnvelope(true, payload)).ConfigureAwait(false);
+ return;
+ }
+
if (string.Equals(method, "POST", StringComparison.OrdinalIgnoreCase) &&
string.Equals(request.Path, "/api/test/export-debug-snapshot", StringComparison.OrdinalIgnoreCase))
{
@@ -697,6 +705,81 @@ namespace NavisworksTransport.Core.Services
///
/// 路径分析(效率/安全评分),集成测试验证分析链路与评分结构。
///
+ ///
+ /// 剖面盒导出(T16):mode=bbox 用指定包围盒导出 NWD(集成测试可控,不依赖 UI 剖面盒状态);
+ /// mode=activeView 用当前视图剖面盒(需 UI 已激活 Box 模式)。
+ ///
+ private static object ExportSectionBoxPayload(Dictionary query)
+ {
+ Document document = Autodesk.Navisworks.Api.Application.ActiveDocument;
+ if (document == null)
+ {
+ throw new InvalidOperationException("当前没有活动文档");
+ }
+
+ string mode = GetOptionalQueryValue(query, "mode") ?? "bbox";
+ string filePath = GetOptionalQueryValue(query, "path");
+ if (string.IsNullOrWhiteSpace(filePath))
+ {
+ filePath = Path.Combine(Path.GetTempPath(), $"sectionbox-export-{Guid.NewGuid():N}.nwd");
+ }
+
+ var exporter = new SectionBoxExporter();
+
+ if (string.Equals(mode, "bbox", StringComparison.OrdinalIgnoreCase))
+ {
+ double x1 = ParseRequiredDouble(query, "x1");
+ double y1 = ParseRequiredDouble(query, "y1");
+ double z1 = ParseRequiredDouble(query, "z1");
+ double x2 = ParseRequiredDouble(query, "x2");
+ double y2 = ParseRequiredDouble(query, "y2");
+ double z2 = ParseRequiredDouble(query, "z2");
+
+ var bounds = new BoundingBox3D(
+ new Point3D(Math.Min(x1, x2), Math.Min(y1, y2), Math.Min(z1, z2)),
+ new Point3D(Math.Max(x1, x2), Math.Max(y1, y2), Math.Max(z1, z2)));
+
+ var traversal = exporter.GetObjectsAndHiddenItems(document, bounds);
+ if (traversal.ObjectsInBox.Count == 0)
+ {
+ throw new InvalidOperationException("指定包围盒内没有找到对象");
+ }
+
+ string exportedPath = exporter.ExportToNwd(document, traversal, filePath);
+ if (string.IsNullOrEmpty(exportedPath) || !File.Exists(exportedPath))
+ {
+ throw new InvalidOperationException($"NWD 导出失败: {filePath}");
+ }
+
+ LogManager.Info($"[测试HTTP] 剖面盒导出完成(bbox): 对象={traversal.ObjectsInBox.Count}, 隐藏={traversal.ItemsToHide.Count}, 文件={exportedPath}");
+
+ return new
+ {
+ mode = "bbox",
+ filePath = exportedPath,
+ objectCount = traversal.ObjectsInBox.Count,
+ hiddenNodeCount = traversal.ItemsToHide.Count,
+ fileSize = new FileInfo(exportedPath).Length
+ };
+ }
+
+ // activeView 模式:依赖当前视图剖面盒
+ var result = exporter.ExportSectionBoxFromActiveView(document, filePath);
+ if (!result.Success)
+ {
+ throw new InvalidOperationException(result.ErrorMessage ?? "剖面盒导出失败");
+ }
+
+ return new
+ {
+ mode = "activeView",
+ filePath = result.FilePath,
+ objectCount = result.ObjectCount,
+ hiddenNodeCount = result.HiddenNodeCount,
+ fileSize = result.FileSize
+ };
+ }
+
///
/// 路径编辑集成测试端点(T11):action=remove-point/update-point/orthogonalize/remove-loops。
/// 直接操作临时测试路径,验证 PathPlanningManager 编辑链路。