diff --git a/src/Core/Animation/PathAnimationManager.cs b/src/Core/Animation/PathAnimationManager.cs index a41dae9..efa6aaa 100644 --- a/src/Core/Animation/PathAnimationManager.cs +++ b/src/Core/Animation/PathAnimationManager.cs @@ -89,6 +89,7 @@ namespace NavisworksTransport.Core.Animation private static readonly Dictionary> _animationFrameCache = new Dictionary>(); // 动画帧缓存 private static readonly Dictionary> _collisionResultCache = new Dictionary>(); // 碰撞结果缓存 private ModelItem _animatedObject; + private bool _isVirtualVehicle = false; // 是否使用虚拟车辆 private List _pathPoints; private List _manualCollisionTargets = new List(); private bool _manualCollisionOverrideEnabled = false; @@ -1170,7 +1171,8 @@ namespace NavisworksTransport.Core.Animation _detectionGap, _pathName, _currentRouteId, - _animatedObjectName, + _animatedObject, + _isVirtualVehicle, _animationFrameRate, _animationDuration ); @@ -2226,16 +2228,12 @@ namespace NavisworksTransport.Core.Animation /// 动画持续时间(秒) /// 路径名称 /// 路由ID - public void CreateAnimation(ModelItem animatedObject, List pathPoints, double durationSeconds = 10.0, string pathName = "未知路径", string routeId = null) + public void CreateAnimation(ModelItem animatedObject, List pathPoints, double durationSeconds = 10.0, string pathName = "未知路径", string routeId = null, bool isVirtualVehicle = false) { _pathName = pathName; _currentRouteId = routeId; - _animatedObjectName = animatedObject?.DisplayName ?? "未知对象"; + _isVirtualVehicle = isVirtualVehicle; // 设置是否使用虚拟车辆 - LogManager.Info($"[PathAnimationManager.CreateAnimation] 接收到参数: pathName='{pathName}', routeId='{routeId}', animatedObjectName='{_animatedObjectName}'"); - LogManager.Info($"[PathAnimationManager.CreateAnimation] 存储的值: _pathName='{_pathName}', _currentRouteId='{_currentRouteId}'"); - - // 注意:pathPoints参数已废弃,现在使用PathRoute SetupAnimation(animatedObject, durationSeconds, _route); SetState(AnimationState.Ready); } diff --git a/src/Core/Collision/ClashDetectiveIntegration.cs b/src/Core/Collision/ClashDetectiveIntegration.cs index fafc41d..a2b059a 100644 --- a/src/Core/Collision/ClashDetectiveIntegration.cs +++ b/src/Core/Collision/ClashDetectiveIntegration.cs @@ -164,13 +164,38 @@ namespace NavisworksTransport /// 保存ClashDetective结果到数据库 /// private void SaveClashDetectiveResultToDatabase(string pathName, string routeId, List clashResults, - int frameRate, double duration, double detectionGap, string animatedObjectName) + int frameRate, double duration, double detectionGap, + ModelItem animatedObject, bool isVirtualVehicle) { try { var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase(); if (pathDatabase != null) { + // 获取动画对象名称 + string animatedObjectName = "未知对象"; + if (!isVirtualVehicle && animatedObject != null) + { + animatedObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(animatedObject); + } + else if (isVirtualVehicle) + { + animatedObjectName = "虚拟车辆"; + } + + // 设置车辆路径信息 + string vehicleModelItemPath = null; + + if (!isVirtualVehicle && animatedObject != null) + { + // 只有真实车辆才记录路径 + var paths = ModelItemAnalysisHelper.GetModelItemIndexPaths(animatedObject); + if (paths.Count > 0) + { + vehicleModelItemPath = string.Join(";", paths); + } + } + var record = new ClashDetectiveResultRecord { TestName = _currentTestName, @@ -183,10 +208,40 @@ namespace NavisworksTransport Duration = duration, DetectionGap = detectionGap, AnimatedObjectName = animatedObjectName, + IsVirtualVehicle = isVirtualVehicle, + VehicleModelItemPath = vehicleModelItemPath, CreatedAt = DateTime.Now }; - pathDatabase.SaveClashDetectiveResult(record); - LogManager.Info($"ClashDetective结果已保存到数据库"); + var resultId = pathDatabase.SaveClashDetectiveResult(record); + LogManager.Info($"ClashDetective结果已保存到数据库,Id={resultId}, IsVirtualVehicle={isVirtualVehicle}"); + + // 保存被撞物体信息(只保存Item2,不保存车辆Item1) + var collisionObjects = new List(); + + foreach (var collision in clashResults) + { + // 只保存被撞到的物体(Item2) + if (collision.Item2 != null) + { + var paths = ModelItemAnalysisHelper.GetModelItemIndexPaths(collision.Item2); + foreach (var path in paths) + { + collisionObjects.Add(new ClashDetectiveCollisionObjectRecord + { + ResultId = resultId, + ModelItemPath = path, + DisplayName = ModelItemAnalysisHelper.GetSafeDisplayName(collision.Item2), + ObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(collision.Item2) + }); + } + } + } + + if (collisionObjects.Count > 0) + { + pathDatabase.SaveClashDetectiveCollisionObjects(resultId, collisionObjects); + LogManager.Info($"已保存 {collisionObjects.Count} 个被撞物体到数据库"); + } // 触发结果保存事件,通知UI刷新列表 ClashDetectiveResultSaved?.Invoke(this, new ClashDetectiveResultSavedEventArgs(pathName, _currentTestName, clashResults.Count)); @@ -352,11 +407,13 @@ namespace NavisworksTransport /// 检测间隙容差 /// 路径名称 /// 路由ID - /// 动画对象名称 + /// 动画对象(如果是真实车辆) + /// 是否使用虚拟车辆 /// 帧率 /// 动画时长 public void CreateAllAnimationCollisionTests(List precomputedCollisions, double detectionGap = 0.05, - string pathName = "未知路径", string routeId = null, string animatedObjectName = null, + string pathName = "未知路径", string routeId = null, + ModelItem animatedObject = null, bool isVirtualVehicle = false, int frameRate = 30, double duration = 10.0) { try @@ -387,7 +444,6 @@ namespace NavisworksTransport LogManager.Info($"[预计算处理] 原始记录: {precomputedCollisions.Count},有效碰撞: {_animationCollisionCount}"); // 获取动画对象和当前动画终点位置(用于后续恢复) - ModelItem animatedObject = null; Point3D animationEndPosition = new Point3D(0, 0, 0); if (collisionResults.Count > 0) @@ -647,7 +703,7 @@ namespace NavisworksTransport _clashDetectiveCollisionCount = clashResults.Count; // 保存到数据库 - SaveClashDetectiveResultToDatabase(pathName, routeId, clashResults, frameRate, duration, detectionGap, animatedObjectName); + SaveClashDetectiveResultToDatabase(pathName, routeId, clashResults, frameRate, duration, detectionGap, animatedObject, isVirtualVehicle); // 第四步:将分组添加到主测试 if (collisionGroup.Children.Count > 0) diff --git a/src/Core/PathDatabase.cs b/src/Core/PathDatabase.cs index 0639036..f7ba630 100644 --- a/src/Core/PathDatabase.cs +++ b/src/Core/PathDatabase.cs @@ -170,16 +170,32 @@ namespace NavisworksTransport Duration REAL, DetectionGap REAL, AnimatedObjectName TEXT, + IsVirtualVehicle INTEGER, + VehicleModelItemPath TEXT, CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP ) "); + // 7. ClashDetective碰撞对象表 + ExecuteNonQuery(@" + CREATE TABLE IF NOT EXISTS ClashDetectiveCollisionObjects ( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + ResultId INTEGER NOT NULL, + ModelItemPath TEXT NOT NULL, + DisplayName TEXT, + ObjectName TEXT, + FOREIGN KEY(ResultId) REFERENCES ClashDetectiveResults(Id) ON DELETE CASCADE + ) + "); + // 创建索引 ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_reports_route ON CollisionReports(RouteId)"); ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_pathpoints_route ON PathPoints(RouteId)"); ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_pathedges_route ON PathEdges(RouteId)"); ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_clash_path_name ON ClashDetectiveResults(PathName)"); ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_clash_test_time ON ClashDetectiveResults(TestTime DESC)"); + ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_clash_objects_result ON ClashDetectiveCollisionObjects(ResultId)"); + ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_clash_objects_path ON ClashDetectiveCollisionObjects(ModelItemPath)"); } /// @@ -371,7 +387,7 @@ namespace NavisworksTransport /// /// 保存ClashDetective碰撞检测结果 /// - public void SaveClashDetectiveResult(ClashDetectiveResultRecord record) + public int SaveClashDetectiveResult(ClashDetectiveResultRecord record) { try { @@ -390,11 +406,12 @@ namespace NavisworksTransport var sql = @" INSERT INTO ClashDetectiveResults (TestName, PathName, RouteId, TestTime, CollisionCount, AnimationCollisionCount, - FrameRate, Duration, DetectionGap, AnimatedObjectName, CreatedAt) + FrameRate, Duration, DetectionGap, AnimatedObjectName, IsVirtualVehicle, VehicleModelItemPath, CreatedAt) VALUES (@testName, @pathName, @routeId, @testTime, @collisionCount, @animationCollisionCount, - @frameRate, @duration, @detectionGap, @animatedObjectName, @createdAt) + @frameRate, @duration, @detectionGap, @animatedObjectName, @isVirtualVehicle, @vehiclePath, @createdAt) "; + long newId = 0; using (var cmd = new SQLiteCommand(sql, _connection)) { cmd.Parameters.AddWithValue("@testName", record.TestName); @@ -407,11 +424,15 @@ namespace NavisworksTransport cmd.Parameters.AddWithValue("@duration", record.Duration); cmd.Parameters.AddWithValue("@detectionGap", record.DetectionGap); cmd.Parameters.AddWithValue("@animatedObjectName", record.AnimatedObjectName ?? ""); + cmd.Parameters.AddWithValue("@isVirtualVehicle", record.IsVirtualVehicle); + cmd.Parameters.AddWithValue("@vehiclePath", record.VehicleModelItemPath ?? ""); cmd.Parameters.AddWithValue("@createdAt", record.CreatedAt); cmd.ExecuteNonQuery(); + newId = _connection.LastInsertRowId; } - LogManager.Info($"ClashDetective结果已保存: 测试={record.TestName}, 碰撞数={record.CollisionCount}"); + LogManager.Info($"ClashDetective结果已保存: 测试={record.TestName}, 碰撞数={record.CollisionCount}, Id={newId}"); + return (int)newId; } catch (Exception ex) { @@ -541,6 +562,114 @@ namespace NavisworksTransport } } + /// + /// 保存ClashDetective碰撞对象 + /// + public void SaveClashDetectiveCollisionObjects(int resultId, List objects) + { + if (objects == null || objects.Count == 0) + return; + + try + { + using (var transaction = _connection.BeginTransaction()) + { + var sql = @" + INSERT INTO ClashDetectiveCollisionObjects + (ResultId, ModelItemPath, DisplayName, ObjectName) + VALUES (@resultId, @path, @displayName, @objectName) + "; + + foreach (var obj in objects) + { + using (var cmd = new SQLiteCommand(sql, _connection)) + { + cmd.Parameters.AddWithValue("@resultId", resultId); + cmd.Parameters.AddWithValue("@path", obj.ModelItemPath ?? ""); + cmd.Parameters.AddWithValue("@displayName", obj.DisplayName ?? ""); + cmd.Parameters.AddWithValue("@objectName", obj.ObjectName ?? ""); + cmd.ExecuteNonQuery(); + } + } + + transaction.Commit(); + LogManager.Info($"已保存 {objects.Count} 个碰撞对象到数据库,ResultId={resultId}"); + } + } + catch (Exception ex) + { + LogManager.Error($"保存ClashDetective碰撞对象失败: {ex.Message}", ex); + throw; + } + } + + /// + /// 获取指定ClashDetective结果的所有碰撞对象 + /// + public List GetClashDetectiveCollisionObjects(int resultId) + { + var results = new List(); + + try + { + var sql = @" + SELECT Id, ResultId, ModelItemPath, DisplayName, ObjectName + FROM ClashDetectiveCollisionObjects + WHERE ResultId = @resultId + "; + + using (var cmd = new SQLiteCommand(sql, _connection)) + { + cmd.Parameters.AddWithValue("@resultId", resultId); + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + results.Add(new ClashDetectiveCollisionObjectRecord + { + Id = Convert.ToInt32(reader["Id"]), + ResultId = Convert.ToInt32(reader["ResultId"]), + ModelItemPath = reader["ModelItemPath"]?.ToString(), + DisplayName = reader["DisplayName"]?.ToString(), + ObjectName = reader["ObjectName"]?.ToString() + }); + } + } + } + + LogManager.Debug($"获取到 {results.Count} 个碰撞对象记录"); + } + catch (Exception ex) + { + LogManager.Error($"获取ClashDetective碰撞对象失败: {ex.Message}", ex); + } + + return results; + } + + /// + /// 根据ModelItemPath查询碰撞次数 + /// + public int GetCollisionCountByModelItemPath(string modelItemPath) + { + try + { + var sql = "SELECT COUNT(*) FROM ClashDetectiveCollisionObjects WHERE ModelItemPath = @path"; + + using (var cmd = new SQLiteCommand(sql, _connection)) + { + cmd.Parameters.AddWithValue("@path", modelItemPath); + var result = cmd.ExecuteScalar(); + return Convert.ToInt32(result); + } + } + catch (Exception ex) + { + LogManager.Error($"查询碰撞次数失败: {ex.Message}", ex); + return 0; + } + } + #endregion /// @@ -1067,9 +1196,23 @@ namespace NavisworksTransport public double Duration { get; set; } public double DetectionGap { get; set; } public string AnimatedObjectName { get; set; } + public bool IsVirtualVehicle { get; set; } + public string VehicleModelItemPath { get; set; } public DateTime CreatedAt { get; set; } } + /// + /// ClashDetective碰撞对象数据模型 + /// + public class ClashDetectiveCollisionObjectRecord + { + public int Id { get; set; } + public int ResultId { get; set; } + public string ModelItemPath { get; set; } + public string DisplayName { get; set; } + public string ObjectName { get; set; } + } + /// /// 路径分析结果数据模型 /// diff --git a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs index fc8944f..8c49102 100644 --- a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs +++ b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs @@ -2187,7 +2187,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 先设置路径到动画管理器 _pathAnimationManager.SetRoute(pathRoute); - _pathAnimationManager.CreateAnimation(animatedObject, pathPoints, AnimationDuration, CurrentPathRoute.Name, CurrentPathRoute.Id); + _pathAnimationManager.CreateAnimation(animatedObject, pathPoints, AnimationDuration, CurrentPathRoute.Name, CurrentPathRoute.Id, UseVirtualVehicle); var totalElapsed = (DateTime.Now - cacheStartTime).TotalMilliseconds; LogManager.Info($"[动画生成] 动画生成完成,总耗时: {totalElapsed:F1}ms");