增加碰撞报告HTML生成器,支持自动导出HTML报告功能
This commit is contained in:
parent
1e1d740aef
commit
d0ade35042
@ -175,6 +175,7 @@
|
||||
<Compile Include="src\Core\FloorAttributeManager.cs" />
|
||||
<!-- Utilities -->
|
||||
<Compile Include="src\Utils\ModelHighlightHelper.cs" />
|
||||
<Compile Include="src\Utils\CollisionReportHtmlGenerator.cs" />
|
||||
<!-- PathPlanning - Auto Path Planning -->
|
||||
<Compile Include="src\PathPlanning\GridMap.cs" />
|
||||
<Compile Include="src\PathPlanning\GridMapGenerator.cs" />
|
||||
|
||||
@ -269,6 +269,21 @@ namespace NavisworksTransport.Commands
|
||||
|
||||
// 显示报告(UI线程)
|
||||
ShowReport(result);
|
||||
|
||||
// 🔥 自动导出HTML报告
|
||||
try
|
||||
{
|
||||
string htmlPath = Utils.CollisionReportHtmlGenerator.AutoSaveHtmlReport(result);
|
||||
if (!string.IsNullOrEmpty(htmlPath))
|
||||
{
|
||||
LogManager.Info($"HTML报告已自动导出: {htmlPath}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"自动导出HTML报告失败: {ex.Message}");
|
||||
// HTML导出失败不影响报告生成
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
||||
296
src/Utils/CollisionReportHtmlGenerator.cs
Normal file
296
src/Utils/CollisionReportHtmlGenerator.cs
Normal file
@ -0,0 +1,296 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NavisworksTransport.Commands;
|
||||
|
||||
namespace NavisworksTransport.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// 碰撞报告HTML生成器
|
||||
/// </summary>
|
||||
public static class CollisionReportHtmlGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// 生成HTML报告内容
|
||||
/// </summary>
|
||||
/// <param name="report">碰撞报告结果</param>
|
||||
/// <param name="htmlFilePath">HTML文件路径(用于计算截图相对路径)</param>
|
||||
/// <returns>HTML内容</returns>
|
||||
public static string GenerateHtmlReport(CollisionReportResult report, string htmlFilePath)
|
||||
{
|
||||
if (report == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(report));
|
||||
}
|
||||
|
||||
var html = new StringBuilder();
|
||||
var now = DateTime.Now;
|
||||
|
||||
// HTML头部和样式
|
||||
html.AppendLine("<!DOCTYPE html>");
|
||||
html.AppendLine("<html><head>");
|
||||
html.AppendLine("<meta charset='UTF-8'>");
|
||||
html.AppendLine("<title>NavisworksTransport 碰撞检测报告</title>");
|
||||
html.AppendLine("<style>");
|
||||
html.AppendLine("body { font-family: 'Microsoft YaHei', Arial, sans-serif; margin: 20px; line-height: 1.6; }");
|
||||
html.AppendLine("h1 { color: #2c5aa0; text-align: center; border-bottom: 2px solid #2c5aa0; padding-bottom: 10px; }");
|
||||
html.AppendLine("h2 { color: #2c5aa0; margin-top: 25px; }");
|
||||
html.AppendLine(".summary { background: #f8fbff; padding: 15px; border-radius: 8px; margin: 15px 0; border-left: 4px solid #2c5aa0; }");
|
||||
html.AppendLine(".stats-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin: 15px 0; }");
|
||||
html.AppendLine(".stat-item { background: white; padding: 15px; border-radius: 6px; border: 1px solid #ddd; text-align: center; }");
|
||||
html.AppendLine(".stat-label { font-size: 12px; color: #666; margin-bottom: 5px; }");
|
||||
html.AppendLine(".stat-value { font-size: 18px; font-weight: bold; color: #2c5aa0; }");
|
||||
html.AppendLine(".moving-object { background: #f0f8ff; padding: 15px; border-radius: 8px; text-align: center; margin: 15px 0; border: 1px solid #b0d4f1; }");
|
||||
html.AppendLine(".collided-list { background: #f8fbff; padding: 15px; border-radius: 8px; margin: 15px 0; max-height: 200px; overflow-y: auto; }");
|
||||
html.AppendLine(".list-item { margin: 5px 0; padding: 5px; border-left: 3px solid #2c5aa0; padding-left: 10px; }");
|
||||
html.AppendLine(".recommendation { background: #fff3cd; padding: 15px; border-radius: 8px; border-left: 4px solid #ffc107; margin: 15px 0; }");
|
||||
html.AppendLine(".collision-item { background: white; margin: 10px 0; padding: 15px; border-radius: 6px; border: 1px solid #eee; }");
|
||||
html.AppendLine(".collision-title { font-weight: bold; color: #333; margin-bottom: 8px; }");
|
||||
html.AppendLine(".collision-status { display: inline-block; padding: 3px 8px; border-radius: 12px; font-size: 11px; color: white; margin-bottom: 8px; }");
|
||||
html.AppendLine(".status-new { background-color: #ff6b6b; } .status-active { background-color: #ffa500; }");
|
||||
html.AppendLine(".status-reviewed { background-color: #87ceeb; } .status-approved { background-color: #98fb98; } .status-resolved { background-color: #90ee90; }");
|
||||
html.AppendLine(".screenshot-section { margin: 20px 0; padding: 15px; background-color: #f5f5f5; border-radius: 5px; }");
|
||||
html.AppendLine(".screenshot-container { text-align: center; }");
|
||||
html.AppendLine(".screenshot-image { max-width: 100%; height: auto; border: 1px solid #ddd; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }");
|
||||
html.AppendLine(".screenshot-info { margin-top: 10px; font-size: 12px; color: #666; }");
|
||||
html.AppendLine("</style>");
|
||||
html.AppendLine("</head><body>");
|
||||
|
||||
// 报告标题
|
||||
html.AppendLine($"<h1>NavisworksTransport 碰撞检测报告</h1>");
|
||||
html.AppendLine($"<p style='text-align: center; color: #666;'>生成时间: {now:yyyy年MM月dd日 HH:mm:ss}</p>");
|
||||
html.AppendLine($"<p style='text-align: center; color: #666;'>报告类型: 综合报告</p>");
|
||||
html.AppendLine($"<p style='text-align: center; color: #666;'>路径名称: {report.PathName ?? "未知路径"}</p>");
|
||||
|
||||
// 总体统计
|
||||
html.AppendLine("<div class='summary'>");
|
||||
html.AppendLine("<h2>总体统计</h2>");
|
||||
html.AppendLine("<div class='stats-grid'>");
|
||||
html.AppendLine("<div class='stat-item'>");
|
||||
html.AppendLine("<div class='stat-label'>总碰撞数</div>");
|
||||
html.AppendLine($"<div class='stat-value'>{report.TotalCollisions}</div>");
|
||||
html.AppendLine("</div>");
|
||||
html.AppendLine("<div class='stat-item'>");
|
||||
html.AppendLine("<div class='stat-label'>碰撞构件</div>");
|
||||
html.AppendLine($"<div class='stat-value'>{report.UniqueCollidedObjectsCount}个</div>");
|
||||
html.AppendLine("</div>");
|
||||
html.AppendLine("<div class='stat-item'>");
|
||||
html.AppendLine("<div class='stat-label'>检测点</div>");
|
||||
html.AppendLine($"<div class='stat-value'>{report.AnimationCollisions}个</div>");
|
||||
html.AppendLine("</div>");
|
||||
html.AppendLine("</div>");
|
||||
html.AppendLine("</div>");
|
||||
|
||||
// 动画参数
|
||||
html.AppendLine("<h2>动画参数</h2>");
|
||||
html.AppendLine("<div class='stats-grid'>");
|
||||
html.AppendLine("<div class='stat-item'>");
|
||||
html.AppendLine("<div class='stat-label'>帧率</div>");
|
||||
html.AppendLine($"<div class='stat-value'>{report.FrameRate} FPS</div>");
|
||||
html.AppendLine("</div>");
|
||||
html.AppendLine("<div class='stat-item'>");
|
||||
html.AppendLine("<div class='stat-label'>时长</div>");
|
||||
html.AppendLine($"<div class='stat-value'>{report.Duration:F1} 秒</div>");
|
||||
html.AppendLine("</div>");
|
||||
html.AppendLine("<div class='stat-item'>");
|
||||
html.AppendLine("<div class='stat-label'>检测容差</div>");
|
||||
html.AppendLine($"<div class='stat-value'>{report.DetectionTolerance:F2} 米</div>");
|
||||
html.AppendLine("</div>");
|
||||
html.AppendLine("</div>");
|
||||
|
||||
// 碰撞场景截图
|
||||
if (!string.IsNullOrEmpty(report.ScreenshotPath) && File.Exists(report.ScreenshotPath))
|
||||
{
|
||||
html.AppendLine("<h2>碰撞场景截图</h2>");
|
||||
html.AppendLine("<div class='screenshot-section'>");
|
||||
|
||||
// 使用 PathHelper 计算相对路径
|
||||
string relativePath = PathHelper.GetRelativePath(htmlFilePath, report.ScreenshotPath);
|
||||
|
||||
html.AppendLine("<div class='screenshot-container'>");
|
||||
html.AppendLine($"<img src=\"{relativePath}\" alt=\"碰撞场景截图\" class=\"screenshot-image\"/>");
|
||||
html.AppendLine("<div class='screenshot-info'>");
|
||||
html.AppendLine($"<p>分辨率: {report.ScreenshotWidth} x {report.ScreenshotHeight}</p>");
|
||||
html.AppendLine($"<p>格式: {report.ScreenshotFormat}</p>");
|
||||
html.AppendLine("</div>");
|
||||
html.AppendLine("</div>");
|
||||
html.AppendLine("</div>");
|
||||
}
|
||||
|
||||
// 运动构件信息
|
||||
if (!string.IsNullOrEmpty(report.MovingObjectInfo))
|
||||
{
|
||||
html.AppendLine("<h2>运动构件信息</h2>");
|
||||
html.AppendLine("<div class='moving-object'>");
|
||||
html.AppendLine($"<strong>🚛 {report.MovingObjectInfo}</strong>");
|
||||
html.AppendLine("</div>");
|
||||
}
|
||||
|
||||
// 碰撞构件清单
|
||||
if (report.CollidedObjectsList?.Count > 0)
|
||||
{
|
||||
html.AppendLine("<h2>碰撞构件清单</h2>");
|
||||
html.AppendLine("<div class='collided-list'>");
|
||||
int index = 1;
|
||||
foreach (var objName in report.CollidedObjectsList.Take(50)) // 限制显示前50个
|
||||
{
|
||||
html.AppendLine($"<div class='list-item'>{index}. {objName}</div>");
|
||||
index++;
|
||||
}
|
||||
if (report.CollidedObjectsList.Count > 50)
|
||||
{
|
||||
html.AppendLine($"<div class='list-item'><em>... 还有 {report.CollidedObjectsList.Count - 50} 个构件</em></div>");
|
||||
}
|
||||
html.AppendLine("</div>");
|
||||
}
|
||||
|
||||
// 总结与建议
|
||||
html.AppendLine("<h2>总结与建议</h2>");
|
||||
html.AppendLine("<div class='recommendation'>");
|
||||
|
||||
// 根据碰撞数量生成总结信息
|
||||
string summaryMessage;
|
||||
if (report.TotalCollisions == 0)
|
||||
{
|
||||
summaryMessage = "✅ 未检测到碰撞,路径规划合理,可以安全通行。";
|
||||
}
|
||||
else if (report.TotalCollisions <= 5)
|
||||
{
|
||||
summaryMessage = $"⚠️ 检测到 {report.TotalCollisions} 个碰撞,建议优化路径或调整构件位置。";
|
||||
}
|
||||
else
|
||||
{
|
||||
summaryMessage = $"🔴 检测到 {report.TotalCollisions} 个碰撞,存在严重安全隐患,必须立即处理。";
|
||||
}
|
||||
|
||||
html.AppendLine($"<p><strong>{summaryMessage}</strong></p>");
|
||||
|
||||
// 生成建议措施
|
||||
html.AppendLine("<p><strong>建议措施:</strong></p>");
|
||||
html.AppendLine("<ul>");
|
||||
|
||||
if (report.TotalCollisions == 0)
|
||||
{
|
||||
html.AppendLine("<li>保持当前路径规划方案</li>");
|
||||
html.AppendLine("<li>定期检查构件位置变化</li>");
|
||||
html.AppendLine("<li>建议进行现场复核确认</li>");
|
||||
}
|
||||
else
|
||||
{
|
||||
html.AppendLine("<li>检查碰撞构件的实际位置和尺寸</li>");
|
||||
html.AppendLine("<li>优化路径规划,避开碰撞区域</li>");
|
||||
html.AppendLine("<li>调整构件位置或尺寸</li>");
|
||||
html.AppendLine("<li>考虑增加安全间隙</li>");
|
||||
html.AppendLine("<li>进行现场勘察确认</li>");
|
||||
}
|
||||
|
||||
html.AppendLine("</ul>");
|
||||
html.AppendLine("</div>");
|
||||
|
||||
// 详细碰撞信息(前10个)
|
||||
if (report.AllCollisions?.Count > 0)
|
||||
{
|
||||
html.AppendLine("<h2>碰撞详情</h2>");
|
||||
int collisionIndex = 1;
|
||||
foreach (var collision in report.AllCollisions.Take(10))
|
||||
{
|
||||
html.AppendLine("<div class='collision-item'>");
|
||||
|
||||
// 生成碰撞标题
|
||||
string item1Name = collision.Item1 != null ? ModelItemAnalysisHelper.GetSafeDisplayName(collision.Item1) : "未知对象";
|
||||
string item2Name = collision.Item2 != null ? ModelItemAnalysisHelper.GetSafeDisplayName(collision.Item2) : "未知对象";
|
||||
html.AppendLine($"<div class='collision-title'>{collisionIndex}. {item1Name} ↔ {item2Name}</div>");
|
||||
|
||||
// 碰撞距离
|
||||
html.AppendLine($"<p><strong>碰撞距离:</strong> {collision.Distance:F3} 米</p>");
|
||||
|
||||
// 碰撞位置
|
||||
if (collision.Center != null)
|
||||
{
|
||||
html.AppendLine($"<p><strong>碰撞位置:</strong> ({collision.Center.X:F2}, {collision.Center.Y:F2}, {collision.Center.Z:F2})</p>");
|
||||
}
|
||||
|
||||
html.AppendLine("</div>");
|
||||
collisionIndex++;
|
||||
}
|
||||
if (report.AllCollisions.Count > 10)
|
||||
{
|
||||
html.AppendLine($"<p><em>... 还有 {report.AllCollisions.Count - 10} 个碰撞未显示</em></p>");
|
||||
}
|
||||
}
|
||||
|
||||
// HTML结尾
|
||||
html.AppendLine($"<hr style='margin: 30px 0;'>");
|
||||
html.AppendLine($"<p style='text-align: center; color: #999; font-size: 12px;'>报告生成完成 - {now:HH:mm:ss}</p>");
|
||||
html.AppendLine("</body></html>");
|
||||
|
||||
return html.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存HTML报告到文件
|
||||
/// </summary>
|
||||
/// <param name="report">碰撞报告结果</param>
|
||||
/// <param name="htmlFilePath">HTML文件保存路径</param>
|
||||
/// <returns>是否保存成功</returns>
|
||||
public static bool SaveHtmlReport(CollisionReportResult report, string htmlFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 确保目录存在
|
||||
string directory = Path.GetDirectoryName(htmlFilePath);
|
||||
if (!string.IsNullOrEmpty(directory))
|
||||
{
|
||||
PathHelper.EnsureDirectoryExists(directory);
|
||||
}
|
||||
|
||||
// 生成HTML内容
|
||||
string htmlContent = GenerateHtmlReport(report, htmlFilePath);
|
||||
|
||||
// 保存到文件
|
||||
File.WriteAllText(htmlFilePath, htmlContent, System.Text.Encoding.UTF8);
|
||||
|
||||
LogManager.Info($"HTML报告已保存: {htmlFilePath}");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"保存HTML报告失败: {ex.Message}", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自动生成并保存HTML报告到默认目录
|
||||
/// </summary>
|
||||
/// <param name="report">碰撞报告结果</param>
|
||||
/// <returns>HTML文件路径,失败返回null</returns>
|
||||
public static string AutoSaveHtmlReport(CollisionReportResult report)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 生成文件名
|
||||
string sanitizedName = PathHelper.SanitizeFileName(report.PathName ?? "未知路径");
|
||||
string fileName = PathHelper.GenerateTimestampedFileName($"collision_{sanitizedName}", "html");
|
||||
|
||||
// 获取报告目录
|
||||
string reportDir = PathHelper.GetReportDirectory();
|
||||
string htmlFilePath = Path.Combine(reportDir, fileName);
|
||||
|
||||
// 保存报告
|
||||
if (SaveHtmlReport(report, htmlFilePath))
|
||||
{
|
||||
return htmlFilePath;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"自动保存HTML报告失败: {ex.Message}", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,6 +33,14 @@ namespace NavisworksTransport.Utils
|
||||
return Path.Combine(GetPluginDirectory(), "screenshots");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取报告目录路径
|
||||
/// </summary>
|
||||
public static string GetReportDirectory()
|
||||
{
|
||||
return Path.Combine(GetPluginDirectory(), "reports");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保目录存在,不存在则创建
|
||||
/// </summary>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user