增加检测记录表,完善批处理过程
This commit is contained in:
parent
749eb07cef
commit
dc1380d7fe
@ -124,6 +124,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
|
||||
// === 碰撞测试优化 ===
|
||||
private string _currentAnimationHash; // 当前动画配置的哈希值
|
||||
private int? _currentDetectionRecordId; // 当前检测记录ID(关联CollisionDetectionRecords表)
|
||||
|
||||
// === 动画播放机制 ===
|
||||
private double _frameInterval; // 帧间隔(毫秒)
|
||||
@ -174,6 +175,15 @@ namespace NavisworksTransport.Core.Animation
|
||||
/// </summary>
|
||||
public ModelItem AnimatedObject => _animatedObject;
|
||||
|
||||
/// <summary>
|
||||
/// 当前检测记录ID(关联CollisionDetectionRecords表)
|
||||
/// </summary>
|
||||
public int? CurrentDetectionRecordId
|
||||
{
|
||||
get => _currentDetectionRecordId;
|
||||
set => _currentDetectionRecordId = value;
|
||||
}
|
||||
|
||||
// --- 新增事件 ---
|
||||
/// <summary>
|
||||
/// 当动画状态发生改变时触发
|
||||
@ -1655,7 +1665,8 @@ namespace NavisworksTransport.Core.Animation
|
||||
_virtualObjectLength,
|
||||
_virtualObjectWidth,
|
||||
_virtualObjectHeight,
|
||||
_pathPoints
|
||||
_pathPoints,
|
||||
_currentDetectionRecordId
|
||||
);
|
||||
}
|
||||
finally
|
||||
|
||||
@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using NavisworksTransport.Core.Animation;
|
||||
using NavisworksTransport.Core.Collision;
|
||||
using NavisworksTransport.Core.Models;
|
||||
|
||||
@ -507,6 +508,19 @@ namespace NavisworksTransport.Core
|
||||
// 统一准备碰撞检测(根据模式自动决定是否构建全局缓存)
|
||||
ClashDetectiveIntegration.PrepareCollisionDetection(animatedObject, isManualMode, manualDetectionTargets);
|
||||
|
||||
// 🔥 从数据库加载该队列项的检测记录(包括排除列表和手工目标)
|
||||
var detectionRecord = await LoadDetectionRecordForQueueItemAsync(item);
|
||||
var excludedObjects = detectionRecord?.ExcludedObjects ?? new List<ModelItem>();
|
||||
var manualTargetsFromRecord = detectionRecord?.ManualTargets ?? new List<ModelItem>();
|
||||
LogManager.Info($"[批处理] 队列项 {item.Id} (DetectionRecordId={item.DetectionRecordId}) 加载了 {excludedObjects.Count} 个排除对象, {manualTargetsFromRecord.Count} 个手工目标");
|
||||
|
||||
// 如果检测记录中有手工目标,使用检测记录中的(优先级高于队列项中的)
|
||||
if (manualTargetsFromRecord.Count > 0)
|
||||
{
|
||||
manualDetectionTargets = manualTargetsFromRecord;
|
||||
isManualMode = true;
|
||||
}
|
||||
|
||||
// 在主线程执行Navisworks API调用
|
||||
var result = await UIStateManager.Instance.ExecuteUIUpdateAsync(() =>
|
||||
{
|
||||
@ -522,6 +536,7 @@ namespace NavisworksTransport.Core
|
||||
CollisionDetectionEnabled = true,
|
||||
ReportGenerationEnabled = true
|
||||
};
|
||||
|
||||
var frames = _processor.PrecomputeFrames(
|
||||
pathRoute,
|
||||
animatedObject,
|
||||
@ -533,7 +548,8 @@ namespace NavisworksTransport.Core
|
||||
config.DurationSeconds,
|
||||
config.DetectionToleranceMeters,
|
||||
manualDetectionTargets,
|
||||
item.ObjectRotationCorrection
|
||||
item.ObjectRotationCorrection,
|
||||
excludedObjects
|
||||
);
|
||||
|
||||
// 检查预计算是否成功
|
||||
@ -561,7 +577,8 @@ namespace NavisworksTransport.Core
|
||||
item.VirtualObjectLength,
|
||||
item.VirtualObjectWidth,
|
||||
item.VirtualObjectHeight,
|
||||
pathPoints
|
||||
pathPoints,
|
||||
item.DetectionRecordId
|
||||
);
|
||||
|
||||
// 🔥 使用 ClashDetective 确认的碰撞数,而不是预计算的碰撞数
|
||||
@ -720,7 +737,331 @@ namespace NavisworksTransport.Core
|
||||
await _database.DeleteBatchQueueItemAsync(itemId);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 检查并加载排除列表(已废弃,使用 LoadExcludedObjectsForQueueItemAsync 代替)
|
||||
/// </summary>
|
||||
/// <returns>排除列表</returns>
|
||||
private List<ModelItem> CheckAndLoadExcludedObjects()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 首先检查 PathAnimationManager 中是否已有排除列表
|
||||
var animationManager = PathAnimationManager.GetInstance();
|
||||
var existingExcludedObjects = animationManager.GetExcludedObjects();
|
||||
|
||||
if (existingExcludedObjects != null && existingExcludedObjects.Count > 0)
|
||||
{
|
||||
LogManager.Info($"[批处理队列] 使用 PathAnimationManager 中的排除列表,共 {existingExcludedObjects.Count} 个物体");
|
||||
return existingExcludedObjects.ToList();
|
||||
}
|
||||
|
||||
// 从数据库加载排除列表(全局排除列表)
|
||||
if (_database != null)
|
||||
{
|
||||
var excludedRecords = _database.GetExcludedObjects(routeId: null, includeGlobal: true);
|
||||
if (excludedRecords != null && excludedRecords.Count > 0)
|
||||
{
|
||||
// 将记录转换为 ModelItem
|
||||
var excludedObjects = new List<ModelItem>();
|
||||
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||||
|
||||
foreach (var record in excludedRecords)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pathIdObj = new Autodesk.Navisworks.Api.DocumentParts.ModelItemPathId
|
||||
{
|
||||
ModelIndex = record.ModelIndex,
|
||||
PathId = record.PathId
|
||||
};
|
||||
var modelItem = doc.Models.ResolvePathId(pathIdObj);
|
||||
if (modelItem != null)
|
||||
{
|
||||
excludedObjects.Add(modelItem);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[批处理队列] 加载排除对象失败: {record.ObjectName}, 错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
if (excludedObjects.Count > 0)
|
||||
{
|
||||
LogManager.Info($"[批处理队列] 从数据库加载排除列表,共 {excludedObjects.Count} 个物体");
|
||||
return excludedObjects;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 排除列表为空,提示用户
|
||||
LogManager.Warning("[批处理队列] 排除列表为空,需要用户确认");
|
||||
|
||||
bool shouldContinue = false;
|
||||
System.Windows.Application.Current?.Dispatcher.Invoke(() =>
|
||||
{
|
||||
var result = System.Windows.MessageBox.Show(
|
||||
"排除列表为空。对于复杂路径,建议先运行\"生成动画\"进行预计算碰撞分析,设置排除列表后再进行批处理。\n\n" +
|
||||
"• 是:继续批处理(不使用排除列表)\n" +
|
||||
"• 否:取消批处理,先去设置排除列表\n\n" +
|
||||
"提示:排除列表可以显著减少复杂路径的碰撞检测时间。",
|
||||
"批处理确认",
|
||||
System.Windows.MessageBoxButton.YesNo,
|
||||
System.Windows.MessageBoxImage.Warning
|
||||
);
|
||||
shouldContinue = (result == System.Windows.MessageBoxResult.Yes);
|
||||
});
|
||||
|
||||
if (shouldContinue)
|
||||
{
|
||||
LogManager.Info("[批处理队列] 用户选择继续批处理(不使用排除列表)");
|
||||
return new List<ModelItem>(); // 返回空列表,允许继续
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info("[批处理队列] 用户选择取消批处理,去设置排除列表");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[批处理队列] 检查排除列表失败: {ex.Message}");
|
||||
// 发生错误时,允许继续(不阻止批处理)
|
||||
return new List<ModelItem>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取批处理用的排除列表(已废弃,使用 LoadExcludedObjectsForQueueItemAsync 代替)
|
||||
/// </summary>
|
||||
private List<ModelItem> GetExcludedObjectsForBatchProcessing()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 首先检查 PathAnimationManager 中是否已有排除列表
|
||||
var animationManager = PathAnimationManager.GetInstance();
|
||||
var existingExcludedObjects = animationManager.GetExcludedObjects();
|
||||
|
||||
if (existingExcludedObjects != null && existingExcludedObjects.Count > 0)
|
||||
{
|
||||
return existingExcludedObjects.ToList();
|
||||
}
|
||||
|
||||
// 从数据库加载排除列表(全局排除列表)
|
||||
if (_database != null)
|
||||
{
|
||||
var excludedRecords = _database.GetExcludedObjects(routeId: null, includeGlobal: true);
|
||||
if (excludedRecords != null && excludedRecords.Count > 0)
|
||||
{
|
||||
var excludedObjects = new List<ModelItem>();
|
||||
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||||
|
||||
foreach (var record in excludedRecords)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pathIdObj = new Autodesk.Navisworks.Api.DocumentParts.ModelItemPathId
|
||||
{
|
||||
ModelIndex = record.ModelIndex,
|
||||
PathId = record.PathId
|
||||
};
|
||||
var modelItem = doc.Models.ResolvePathId(pathIdObj);
|
||||
if (modelItem != null)
|
||||
{
|
||||
excludedObjects.Add(modelItem);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[批处理队列] 加载排除对象失败: {record.ObjectName}, 错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return excludedObjects;
|
||||
}
|
||||
}
|
||||
|
||||
return new List<ModelItem>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[批处理队列] 获取排除列表失败: {ex.Message}");
|
||||
return new List<ModelItem>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从数据库加载指定队列项的排除列表
|
||||
/// 从 ExcludedObjects 表读取,根据队列项对应的路径ID (RouteId) 获取
|
||||
/// </summary>
|
||||
private async Task<List<ModelItem>> LoadExcludedObjectsForQueueItemAsync(BatchQueueItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_database == null)
|
||||
{
|
||||
LogManager.Warning($"[批处理队列] 数据库未初始化,无法加载队列项 {item.Id} 的排除列表");
|
||||
return new List<ModelItem>();
|
||||
}
|
||||
|
||||
// 从 ExcludedObjects 表加载该路径的排除对象
|
||||
// 排除列表在生成动画时已保存,按 RouteId 关联
|
||||
var excludedRecords = await Task.Run(() =>
|
||||
_database.GetExcludedObjects(item.RouteId, includeGlobal: true));
|
||||
|
||||
if (excludedRecords == null || excludedRecords.Count == 0)
|
||||
{
|
||||
LogManager.Info($"[批处理队列] 队列项 {item.Id} (RouteId={item.RouteId}) 没有排除对象");
|
||||
return new List<ModelItem>();
|
||||
}
|
||||
|
||||
var excludedObjects = new List<ModelItem>();
|
||||
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||||
|
||||
foreach (var record in excludedRecords)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pathIdObj = new Autodesk.Navisworks.Api.DocumentParts.ModelItemPathId
|
||||
{
|
||||
ModelIndex = record.ModelIndex,
|
||||
PathId = record.PathId
|
||||
};
|
||||
var modelItem = doc.Models.ResolvePathId(pathIdObj);
|
||||
if (modelItem != null)
|
||||
{
|
||||
excludedObjects.Add(modelItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Warning($"[批处理队列] 无法通过 PathId 找到排除对象: ModelIndex={record.ModelIndex}, PathId={record.PathId}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[批处理队列] 加载排除对象失败: {record.ObjectName}, 错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"[批处理队列] 队列项 {item.Id} 加载了 {excludedObjects.Count}/{excludedRecords.Count} 个排除对象");
|
||||
return excludedObjects;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[批处理队列] 加载队列项 {item.Id} 的排除列表失败: {ex.Message}");
|
||||
return new List<ModelItem>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载队列项关联的检测记录(包含排除列表和手工目标)
|
||||
/// </summary>
|
||||
private async Task<DetectionRecordData> LoadDetectionRecordForQueueItemAsync(BatchQueueItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_database == null || !item.DetectionRecordId.HasValue)
|
||||
{
|
||||
LogManager.Warning($"[批处理队列] 队列项 {item.Id} 没有关联的检测记录");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取检测记录
|
||||
var record = await Task.Run(() => _database.GetCollisionDetectionRecord(item.DetectionRecordId.Value));
|
||||
if (record == null)
|
||||
{
|
||||
LogManager.Warning($"[批处理队列] 未找到检测记录 (Id={item.DetectionRecordId})");
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = new DetectionRecordData
|
||||
{
|
||||
RecordId = record.Id,
|
||||
RouteId = record.RouteId,
|
||||
FrameRate = record.FrameRate,
|
||||
DurationSeconds = record.DurationSeconds,
|
||||
DetectionToleranceMeters = record.DetectionToleranceMeters,
|
||||
IsVirtualObject = record.IsVirtualObject,
|
||||
DetectAllObjects = record.DetectAllObjects,
|
||||
ObjectRotationCorrection = record.ObjectRotationCorrection,
|
||||
ExcludedObjects = new List<ModelItem>(),
|
||||
ManualTargets = new List<ModelItem>()
|
||||
};
|
||||
|
||||
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||||
|
||||
// 加载排除列表
|
||||
var excludedRecords = await Task.Run(() => _database.GetCollisionDetectionExcludedObjects(record.Id));
|
||||
foreach (var excludedRecord in excludedRecords)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pathIdObj = new Autodesk.Navisworks.Api.DocumentParts.ModelItemPathId
|
||||
{
|
||||
ModelIndex = excludedRecord.ModelIndex,
|
||||
PathId = excludedRecord.PathId
|
||||
};
|
||||
var modelItem = doc.Models.ResolvePathId(pathIdObj);
|
||||
if (modelItem != null)
|
||||
{
|
||||
result.ExcludedObjects.Add(modelItem);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[批处理队列] 加载排除对象失败: {excludedRecord.ObjectName}, {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 加载手工目标
|
||||
var manualTargetRecords = await Task.Run(() => _database.GetCollisionDetectionManualTargets(record.Id));
|
||||
foreach (var targetRecord in manualTargetRecords)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pathIdObj = new Autodesk.Navisworks.Api.DocumentParts.ModelItemPathId
|
||||
{
|
||||
ModelIndex = targetRecord.ModelIndex,
|
||||
PathId = targetRecord.PathId
|
||||
};
|
||||
var modelItem = doc.Models.ResolvePathId(pathIdObj);
|
||||
if (modelItem != null)
|
||||
{
|
||||
result.ManualTargets.Add(modelItem);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[批处理队列] 加载手工目标失败: {targetRecord.ObjectName}, {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"[批处理队列] 加载检测记录 (Id={record.Id}): 排除对象={result.ExcludedObjects.Count}, 手工目标={result.ManualTargets.Count}");
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[批处理队列] 加载检测记录失败: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测记录数据(用于批处理)
|
||||
/// </summary>
|
||||
public class DetectionRecordData
|
||||
{
|
||||
public int RecordId { get; set; }
|
||||
public string RouteId { get; set; }
|
||||
public int FrameRate { get; set; }
|
||||
public double DurationSeconds { get; set; }
|
||||
public double DetectionToleranceMeters { get; set; }
|
||||
public bool IsVirtualObject { get; set; }
|
||||
public bool DetectAllObjects { get; set; }
|
||||
public double ObjectRotationCorrection { get; set; }
|
||||
public List<ModelItem> ExcludedObjects { get; set; }
|
||||
public List<ModelItem> ManualTargets { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -34,7 +34,8 @@ namespace NavisworksTransport.Core.Collision
|
||||
double duration,
|
||||
double detectionGap,
|
||||
List<ModelItem> manualDetectionTargets = null,
|
||||
double objectRotationCorrection = 0.0)
|
||||
double objectRotationCorrection = 0.0,
|
||||
List<ModelItem> excludedObjects = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -52,6 +53,13 @@ namespace NavisworksTransport.Core.Collision
|
||||
_animationManager.SetManualCollisionTargets(manualDetectionTargets);
|
||||
}
|
||||
|
||||
// 设置排除列表(关键:批处理时使用排除列表)
|
||||
if (excludedObjects != null && excludedObjects.Count > 0)
|
||||
{
|
||||
_animationManager.SetExcludedObjectsAndClearCache(excludedObjects);
|
||||
LogManager.Info($"[批处理] 已设置排除列表,共 {excludedObjects.Count} 个物体");
|
||||
}
|
||||
|
||||
// 调用预计算方法(内部不涉及UI操作)
|
||||
var frames = _animationManager.PrecomputeAnimationFramesInternal();
|
||||
|
||||
@ -81,7 +89,8 @@ namespace NavisworksTransport.Core.Collision
|
||||
double virtualObjectLength,
|
||||
double virtualObjectWidth,
|
||||
double virtualObjectHeight,
|
||||
List<Point3D> pathPoints = null)
|
||||
List<Point3D> pathPoints = null,
|
||||
int? detectionRecordId = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -99,7 +108,8 @@ namespace NavisworksTransport.Core.Collision
|
||||
virtualObjectLength,
|
||||
virtualObjectWidth,
|
||||
virtualObjectHeight,
|
||||
pathPoints
|
||||
pathPoints,
|
||||
detectionRecordId
|
||||
);
|
||||
|
||||
LogManager.Info($"[批处理] ClashDetective测试创建完成 - 测试名称: {testName}");
|
||||
|
||||
@ -224,7 +224,7 @@ namespace NavisworksTransport
|
||||
int frameRate, double duration, double detectionGap,
|
||||
ModelItem animatedObject, bool isVirtualObject,
|
||||
double virtualObjectLength, double virtualObjectWidth, double virtualObjectHeight,
|
||||
int precomputedCollisionCount = 0)
|
||||
int precomputedCollisionCount = 0, int? detectionRecordId = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -269,7 +269,8 @@ namespace NavisworksTransport
|
||||
VirtualObjectLength = virtualObjectLength,
|
||||
VirtualObjectWidth = virtualObjectWidth,
|
||||
VirtualObjectHeight = virtualObjectHeight,
|
||||
CreatedAt = DateTime.Now
|
||||
CreatedAt = DateTime.Now,
|
||||
DetectionRecordId = detectionRecordId // 关联到检测记录
|
||||
};
|
||||
var resultId = pathDatabase.SaveClashDetectiveResult(record);
|
||||
LogManager.Info($"ClashDetective结果已保存到数据库,Id={resultId}, IsVirtualObject={isVirtualObject}");
|
||||
@ -699,7 +700,8 @@ namespace NavisworksTransport
|
||||
double virtualObjectWidth,
|
||||
double virtualObjectHeight,
|
||||
Progress progress = null,
|
||||
int precomputedCollisionCount = 0)
|
||||
int precomputedCollisionCount = 0,
|
||||
int? detectionRecordId = null)
|
||||
{
|
||||
LogManager.Info($"[ClashDetective] 开始运行碰撞检测并保存到数据库(容差: {detectionGap}米)");
|
||||
|
||||
@ -765,7 +767,8 @@ namespace NavisworksTransport
|
||||
virtualObjectLength,
|
||||
virtualObjectWidth,
|
||||
virtualObjectHeight,
|
||||
precomputedCollisionCount
|
||||
precomputedCollisionCount,
|
||||
detectionRecordId
|
||||
);
|
||||
|
||||
return (null, addedMainTest, 0, false);
|
||||
@ -1122,7 +1125,7 @@ namespace NavisworksTransport
|
||||
|
||||
// 保存到数据库(使用去重后的结果)
|
||||
SaveClashDetectiveResultToDatabase(routeId, finalClashResults, frameRate, duration, detectionGap, animatedObject, isVirtualObject,
|
||||
virtualObjectLength, virtualObjectWidth, virtualObjectHeight, precomputedCollisionCount);
|
||||
virtualObjectLength, virtualObjectWidth, virtualObjectHeight, precomputedCollisionCount, detectionRecordId);
|
||||
|
||||
LogManager.Info($"[ClashDetective] 结果已保存到数据库");
|
||||
|
||||
@ -1159,7 +1162,8 @@ namespace NavisworksTransport
|
||||
double virtualObjectLength,
|
||||
double virtualObjectWidth,
|
||||
double virtualObjectHeight,
|
||||
List<Point3D> pathPoints = null)
|
||||
List<Point3D> pathPoints = null,
|
||||
int? detectionRecordId = null)
|
||||
{
|
||||
// 重置取消标志
|
||||
_wasLastTestCanceled = false;
|
||||
@ -1212,7 +1216,8 @@ namespace NavisworksTransport
|
||||
virtualObjectWidth,
|
||||
virtualObjectHeight,
|
||||
progress,
|
||||
precomputedCollisionCount
|
||||
precomputedCollisionCount,
|
||||
detectionRecordId
|
||||
);
|
||||
collisionGroup = result.collisionGroup;
|
||||
addedMainTest = result.addedMainTest;
|
||||
@ -2127,7 +2132,8 @@ namespace NavisworksTransport
|
||||
double virtualObjectLength,
|
||||
double virtualObjectWidth,
|
||||
double virtualObjectHeight,
|
||||
List<Point3D> pathPoints = null)
|
||||
List<Point3D> pathPoints = null,
|
||||
int? detectionRecordId = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -2152,7 +2158,8 @@ namespace NavisworksTransport
|
||||
virtualObjectWidth,
|
||||
virtualObjectHeight,
|
||||
null,
|
||||
precomputedCollisionCount
|
||||
precomputedCollisionCount,
|
||||
detectionRecordId
|
||||
);
|
||||
|
||||
var collisionGroup = result.collisionGroup;
|
||||
|
||||
@ -24,6 +24,8 @@ namespace NavisworksTransport.Core.Collision
|
||||
/// <param name="duration">持续时间(秒)</param>
|
||||
/// <param name="detectionGap">检测间隙(米)</param>
|
||||
/// <param name="manualDetectionTargets">手动指定的检测目标(可选)</param>
|
||||
/// <param name="objectRotationCorrection">物体旋转修正角度(度)</param>
|
||||
/// <param name="excludedObjects">排除列表(可选)</param>
|
||||
/// <returns>动画帧列表</returns>
|
||||
List<AnimationFrame> PrecomputeFrames(
|
||||
PathRoute route,
|
||||
@ -36,7 +38,8 @@ namespace NavisworksTransport.Core.Collision
|
||||
double duration,
|
||||
double detectionGap,
|
||||
List<ModelItem> manualDetectionTargets = null,
|
||||
double objectRotationCorrection = 0.0);
|
||||
double objectRotationCorrection = 0.0,
|
||||
List<ModelItem> excludedObjects = null);
|
||||
|
||||
/// <summary>
|
||||
/// 创建并运行ClashDetective测试(纯计算,无UI操作)
|
||||
@ -54,6 +57,7 @@ namespace NavisworksTransport.Core.Collision
|
||||
/// <param name="virtualObjectWidth">虚拟物体宽度(米)</param>
|
||||
/// <param name="virtualObjectHeight">虚拟物体高度(米)</param>
|
||||
/// <param name="pathPoints">路径点列表(用于测试完成后恢复物体位置)</param>
|
||||
/// <param name="detectionRecordId">检测记录ID(关联CollisionDetectionRecords表)</param>
|
||||
/// <returns>ClashDetective测试名称</returns>
|
||||
string CreateAndRunClashDetectiveTest(
|
||||
List<CollisionResult> precomputedCollisions,
|
||||
@ -67,6 +71,7 @@ namespace NavisworksTransport.Core.Collision
|
||||
double virtualObjectLength,
|
||||
double virtualObjectWidth,
|
||||
double virtualObjectHeight,
|
||||
List<Point3D> pathPoints = null);
|
||||
List<Point3D> pathPoints = null,
|
||||
int? detectionRecordId = null);
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,9 @@ namespace NavisworksTransport.Core.Models
|
||||
// 角度修正配置
|
||||
public double ObjectRotationCorrection { get; set; }
|
||||
|
||||
// 关联的检测记录ID(每次生成动画时创建)
|
||||
public int? DetectionRecordId { get; set; }
|
||||
|
||||
// 结果
|
||||
public string ClashDetectiveTestName { get; set; }
|
||||
public int? CollisionCount { get; set; }
|
||||
|
||||
@ -200,7 +200,9 @@ namespace NavisworksTransport
|
||||
VirtualObjectLength REAL,
|
||||
VirtualObjectWidth REAL,
|
||||
VirtualObjectHeight REAL,
|
||||
CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
DetectionRecordId INTEGER,
|
||||
FOREIGN KEY(DetectionRecordId) REFERENCES CollisionDetectionRecords(Id) ON DELETE SET NULL
|
||||
)
|
||||
");
|
||||
|
||||
@ -381,6 +383,77 @@ namespace NavisworksTransport
|
||||
");
|
||||
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_clash_excluded_result ON ClashDetectiveExcludedObjects(ResultId)");
|
||||
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_clash_excluded_object ON ClashDetectiveExcludedObjects(ExcludedObjectId)");
|
||||
|
||||
// 13. 碰撞检测记录表(每次点击"生成动画"创建一条记录,保存当时的完整配置)
|
||||
// 先添加新列到 BatchQueueItems 表
|
||||
try
|
||||
{
|
||||
ExecuteNonQuery("ALTER TABLE BatchQueueItems ADD COLUMN DetectionRecordId INTEGER");
|
||||
LogManager.Info("[数据库] 已添加 DetectionRecordId 列到 BatchQueueItems 表");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 列已存在,忽略错误
|
||||
}
|
||||
|
||||
ExecuteNonQuery(@"
|
||||
CREATE TABLE IF NOT EXISTS CollisionDetectionRecords (
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
RouteId TEXT NOT NULL,
|
||||
CreatedTime DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
-- 动画参数
|
||||
FrameRate INTEGER NOT NULL,
|
||||
DurationSeconds REAL NOT NULL,
|
||||
-- 碰撞检测参数
|
||||
DetectionToleranceMeters REAL NOT NULL,
|
||||
-- 运动物体参数
|
||||
IsVirtualObject INTEGER NOT NULL DEFAULT 0,
|
||||
AnimatedObjectName TEXT,
|
||||
ObjectModelIndex INTEGER,
|
||||
ObjectPathId TEXT,
|
||||
VirtualObjectLength REAL,
|
||||
VirtualObjectWidth REAL,
|
||||
VirtualObjectHeight REAL,
|
||||
-- 检测模式
|
||||
DetectAllObjects INTEGER NOT NULL DEFAULT 1,
|
||||
-- 角度修正
|
||||
ObjectRotationCorrection REAL DEFAULT 0.0,
|
||||
-- 备注/描述
|
||||
Description TEXT,
|
||||
FOREIGN KEY(RouteId) REFERENCES PathRoutes(Id) ON DELETE CASCADE
|
||||
)
|
||||
");
|
||||
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_detection_route ON CollisionDetectionRecords(RouteId)");
|
||||
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_detection_time ON CollisionDetectionRecords(CreatedTime DESC)");
|
||||
|
||||
// 14. 碰撞检测记录排除对象关联表(记录每次检测时的排除列表)
|
||||
ExecuteNonQuery(@"
|
||||
CREATE TABLE IF NOT EXISTS CollisionDetectionExcludedObjects (
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
DetectionRecordId INTEGER NOT NULL,
|
||||
ModelIndex INTEGER NOT NULL,
|
||||
PathId TEXT NOT NULL,
|
||||
DisplayName TEXT,
|
||||
ObjectName TEXT,
|
||||
Reason TEXT,
|
||||
FOREIGN KEY(DetectionRecordId) REFERENCES CollisionDetectionRecords(Id) ON DELETE CASCADE
|
||||
)
|
||||
");
|
||||
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_detection_excluded_record ON CollisionDetectionExcludedObjects(DetectionRecordId)");
|
||||
|
||||
// 15. 碰撞检测记录手工目标关联表(记录每次检测时的手工碰撞目标)
|
||||
ExecuteNonQuery(@"
|
||||
CREATE TABLE IF NOT EXISTS CollisionDetectionManualTargets (
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
DetectionRecordId INTEGER NOT NULL,
|
||||
ModelIndex INTEGER NOT NULL,
|
||||
PathId TEXT NOT NULL,
|
||||
DisplayName TEXT,
|
||||
ObjectName TEXT,
|
||||
FOREIGN KEY(DetectionRecordId) REFERENCES CollisionDetectionRecords(Id) ON DELETE CASCADE
|
||||
)
|
||||
");
|
||||
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_detection_manual_record ON CollisionDetectionManualTargets(DetectionRecordId)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -801,10 +874,10 @@ namespace NavisworksTransport
|
||||
INSERT INTO ClashDetectiveResults
|
||||
(TestName, RouteId, TestTime, CollisionCount, AnimationCollisionCount,
|
||||
FrameRate, Duration, DetectionGap, AnimatedObjectName, IsVirtualObject, ObjectModelIndex, ObjectPathId,
|
||||
VirtualObjectLength, VirtualObjectWidth, VirtualObjectHeight, CreatedAt)
|
||||
VirtualObjectLength, VirtualObjectWidth, VirtualObjectHeight, CreatedAt, DetectionRecordId)
|
||||
VALUES (@testName, @routeId, @testTime, @collisionCount, @animationCollisionCount,
|
||||
@frameRate, @duration, @detectionGap, @animatedObjectName, @isVirtualObject, @objectModelIndex, @objectPathId,
|
||||
@virtualObjectLength, @virtualObjectWidth, @virtualObjectHeight, @createdAt)
|
||||
@virtualObjectLength, @virtualObjectWidth, @virtualObjectHeight, @createdAt, @detectionRecordId)
|
||||
";
|
||||
|
||||
long newId = 0;
|
||||
@ -826,6 +899,7 @@ namespace NavisworksTransport
|
||||
cmd.Parameters.AddWithValue("@virtualObjectWidth", record.VirtualObjectWidth);
|
||||
cmd.Parameters.AddWithValue("@virtualObjectHeight", record.VirtualObjectHeight);
|
||||
cmd.Parameters.AddWithValue("@createdAt", record.CreatedAt);
|
||||
cmd.Parameters.AddWithValue("@detectionRecordId", record.DetectionRecordId ?? (object)DBNull.Value);
|
||||
cmd.ExecuteNonQuery();
|
||||
newId = _connection.LastInsertRowId;
|
||||
}
|
||||
@ -2110,6 +2184,321 @@ namespace NavisworksTransport
|
||||
|
||||
#endregion
|
||||
|
||||
#region 碰撞检测记录
|
||||
|
||||
/// <summary>
|
||||
/// 保存碰撞检测记录
|
||||
/// 每次点击"生成动画"创建一条记录
|
||||
/// </summary>
|
||||
public int SaveCollisionDetectionRecord(CollisionDetectionRecord record)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sql = @"
|
||||
INSERT INTO CollisionDetectionRecords (
|
||||
RouteId, CreatedTime, FrameRate, DurationSeconds, DetectionToleranceMeters,
|
||||
IsVirtualObject, AnimatedObjectName, ObjectModelIndex, ObjectPathId,
|
||||
VirtualObjectLength, VirtualObjectWidth, VirtualObjectHeight,
|
||||
DetectAllObjects, ObjectRotationCorrection, Description
|
||||
)
|
||||
VALUES (
|
||||
@RouteId, @CreatedTime, @FrameRate, @DurationSeconds, @DetectionToleranceMeters,
|
||||
@IsVirtualObject, @AnimatedObjectName, @ObjectModelIndex, @ObjectPathId,
|
||||
@VirtualObjectLength, @VirtualObjectWidth, @VirtualObjectHeight,
|
||||
@DetectAllObjects, @ObjectRotationCorrection, @Description
|
||||
);
|
||||
SELECT last_insert_rowid();
|
||||
";
|
||||
|
||||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@RouteId", record.RouteId);
|
||||
cmd.Parameters.AddWithValue("@CreatedTime", record.CreatedTime);
|
||||
cmd.Parameters.AddWithValue("@FrameRate", record.FrameRate);
|
||||
cmd.Parameters.AddWithValue("@DurationSeconds", record.DurationSeconds);
|
||||
cmd.Parameters.AddWithValue("@DetectionToleranceMeters", record.DetectionToleranceMeters);
|
||||
cmd.Parameters.AddWithValue("@IsVirtualObject", record.IsVirtualObject ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@AnimatedObjectName", record.AnimatedObjectName ?? "");
|
||||
cmd.Parameters.AddWithValue("@ObjectModelIndex", record.ObjectModelIndex ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@ObjectPathId", record.ObjectPathId ?? "");
|
||||
cmd.Parameters.AddWithValue("@VirtualObjectLength", record.VirtualObjectLength ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@VirtualObjectWidth", record.VirtualObjectWidth ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@VirtualObjectHeight", record.VirtualObjectHeight ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@DetectAllObjects", record.DetectAllObjects ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@ObjectRotationCorrection", record.ObjectRotationCorrection);
|
||||
cmd.Parameters.AddWithValue("@Description", record.Description ?? "");
|
||||
|
||||
var id = Convert.ToInt32(cmd.ExecuteScalar());
|
||||
LogManager.Info($"保存碰撞检测记录: Id={id}, RouteId={record.RouteId}");
|
||||
return id;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"保存碰撞检测记录失败: {ex.Message}", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存碰撞检测记录的排除对象
|
||||
/// </summary>
|
||||
public void SaveCollisionDetectionExcludedObjects(int detectionRecordId, List<CollisionDetectionExcludedObjectRecord> objects)
|
||||
{
|
||||
if (objects == null || objects.Count == 0) return;
|
||||
|
||||
try
|
||||
{
|
||||
using (var transaction = _connection.BeginTransaction())
|
||||
{
|
||||
var sql = @"
|
||||
INSERT INTO CollisionDetectionExcludedObjects
|
||||
(DetectionRecordId, ModelIndex, PathId, DisplayName, ObjectName, Reason)
|
||||
VALUES (@DetectionRecordId, @ModelIndex, @PathId, @DisplayName, @ObjectName, @Reason)
|
||||
";
|
||||
|
||||
foreach (var obj in objects)
|
||||
{
|
||||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@DetectionRecordId", detectionRecordId);
|
||||
cmd.Parameters.AddWithValue("@ModelIndex", obj.ModelIndex);
|
||||
cmd.Parameters.AddWithValue("@PathId", obj.PathId);
|
||||
cmd.Parameters.AddWithValue("@DisplayName", obj.DisplayName ?? "");
|
||||
cmd.Parameters.AddWithValue("@ObjectName", obj.ObjectName ?? "");
|
||||
cmd.Parameters.AddWithValue("@Reason", obj.Reason ?? "");
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
LogManager.Info($"保存检测记录排除对象: DetectionRecordId={detectionRecordId}, 数量={objects.Count}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"保存检测记录排除对象失败: {ex.Message}", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存碰撞检测记录的手工目标
|
||||
/// </summary>
|
||||
public void SaveCollisionDetectionManualTargets(int detectionRecordId, List<CollisionDetectionManualTargetRecord> targets)
|
||||
{
|
||||
if (targets == null || targets.Count == 0) return;
|
||||
|
||||
try
|
||||
{
|
||||
using (var transaction = _connection.BeginTransaction())
|
||||
{
|
||||
var sql = @"
|
||||
INSERT INTO CollisionDetectionManualTargets
|
||||
(DetectionRecordId, ModelIndex, PathId, DisplayName, ObjectName)
|
||||
VALUES (@DetectionRecordId, @ModelIndex, @PathId, @DisplayName, @ObjectName)
|
||||
";
|
||||
|
||||
foreach (var target in targets)
|
||||
{
|
||||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@DetectionRecordId", detectionRecordId);
|
||||
cmd.Parameters.AddWithValue("@ModelIndex", target.ModelIndex);
|
||||
cmd.Parameters.AddWithValue("@PathId", target.PathId);
|
||||
cmd.Parameters.AddWithValue("@DisplayName", target.DisplayName ?? "");
|
||||
cmd.Parameters.AddWithValue("@ObjectName", target.ObjectName ?? "");
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
LogManager.Info($"保存检测记录手工目标: DetectionRecordId={detectionRecordId}, 数量={targets.Count}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"保存检测记录手工目标失败: {ex.Message}", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取碰撞检测记录
|
||||
/// </summary>
|
||||
public CollisionDetectionRecord GetCollisionDetectionRecord(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sql = @"
|
||||
SELECT * FROM CollisionDetectionRecords WHERE Id = @Id
|
||||
";
|
||||
|
||||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@Id", id);
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
return ReadCollisionDetectionRecordFromReader(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取碰撞检测记录失败: {ex.Message}", ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取路径的最新碰撞检测记录
|
||||
/// </summary>
|
||||
public CollisionDetectionRecord GetLatestCollisionDetectionRecord(string routeId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sql = @"
|
||||
SELECT * FROM CollisionDetectionRecords
|
||||
WHERE RouteId = @RouteId
|
||||
ORDER BY CreatedTime DESC
|
||||
LIMIT 1
|
||||
";
|
||||
|
||||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@RouteId", routeId);
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
return ReadCollisionDetectionRecordFromReader(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取最新碰撞检测记录失败: {ex.Message}", ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取碰撞检测记录的排除对象
|
||||
/// </summary>
|
||||
public List<CollisionDetectionExcludedObjectRecord> GetCollisionDetectionExcludedObjects(int detectionRecordId)
|
||||
{
|
||||
var results = new List<CollisionDetectionExcludedObjectRecord>();
|
||||
|
||||
try
|
||||
{
|
||||
var sql = @"
|
||||
SELECT * FROM CollisionDetectionExcludedObjects
|
||||
WHERE DetectionRecordId = @DetectionRecordId
|
||||
";
|
||||
|
||||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@DetectionRecordId", detectionRecordId);
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
results.Add(new CollisionDetectionExcludedObjectRecord
|
||||
{
|
||||
Id = Convert.ToInt32(reader["Id"]),
|
||||
DetectionRecordId = Convert.ToInt32(reader["DetectionRecordId"]),
|
||||
ModelIndex = Convert.ToInt32(reader["ModelIndex"]),
|
||||
PathId = reader["PathId"].ToString(),
|
||||
DisplayName = reader["DisplayName"]?.ToString(),
|
||||
ObjectName = reader["ObjectName"]?.ToString(),
|
||||
Reason = reader["Reason"]?.ToString()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取检测记录排除对象失败: {ex.Message}", ex);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取碰撞检测记录的手工目标
|
||||
/// </summary>
|
||||
public List<CollisionDetectionManualTargetRecord> GetCollisionDetectionManualTargets(int detectionRecordId)
|
||||
{
|
||||
var results = new List<CollisionDetectionManualTargetRecord>();
|
||||
|
||||
try
|
||||
{
|
||||
var sql = @"
|
||||
SELECT * FROM CollisionDetectionManualTargets
|
||||
WHERE DetectionRecordId = @DetectionRecordId
|
||||
";
|
||||
|
||||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@DetectionRecordId", detectionRecordId);
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
results.Add(new CollisionDetectionManualTargetRecord
|
||||
{
|
||||
Id = Convert.ToInt32(reader["Id"]),
|
||||
DetectionRecordId = Convert.ToInt32(reader["DetectionRecordId"]),
|
||||
ModelIndex = Convert.ToInt32(reader["ModelIndex"]),
|
||||
PathId = reader["PathId"].ToString(),
|
||||
DisplayName = reader["DisplayName"]?.ToString(),
|
||||
ObjectName = reader["ObjectName"]?.ToString()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取检测记录手工目标失败: {ex.Message}", ex);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从DataReader读取碰撞检测记录
|
||||
/// </summary>
|
||||
private CollisionDetectionRecord ReadCollisionDetectionRecordFromReader(SQLiteDataReader reader)
|
||||
{
|
||||
return new CollisionDetectionRecord
|
||||
{
|
||||
Id = Convert.ToInt32(reader["Id"]),
|
||||
RouteId = reader["RouteId"].ToString(),
|
||||
CreatedTime = Convert.ToDateTime(reader["CreatedTime"]),
|
||||
FrameRate = Convert.ToInt32(reader["FrameRate"]),
|
||||
DurationSeconds = Convert.ToDouble(reader["DurationSeconds"]),
|
||||
DetectionToleranceMeters = Convert.ToDouble(reader["DetectionToleranceMeters"]),
|
||||
IsVirtualObject = Convert.ToInt32(reader["IsVirtualObject"]) == 1,
|
||||
AnimatedObjectName = reader["AnimatedObjectName"]?.ToString(),
|
||||
ObjectModelIndex = reader["ObjectModelIndex"] != DBNull.Value ? Convert.ToInt32(reader["ObjectModelIndex"]) : (int?)null,
|
||||
ObjectPathId = reader["ObjectPathId"]?.ToString(),
|
||||
VirtualObjectLength = reader["VirtualObjectLength"] != DBNull.Value ? Convert.ToDouble(reader["VirtualObjectLength"]) : (double?)null,
|
||||
VirtualObjectWidth = reader["VirtualObjectWidth"] != DBNull.Value ? Convert.ToDouble(reader["VirtualObjectWidth"]) : (double?)null,
|
||||
VirtualObjectHeight = reader["VirtualObjectHeight"] != DBNull.Value ? Convert.ToDouble(reader["VirtualObjectHeight"]) : (double?)null,
|
||||
DetectAllObjects = Convert.ToInt32(reader["DetectAllObjects"]) == 1,
|
||||
ObjectRotationCorrection = Convert.ToDouble(reader["ObjectRotationCorrection"]),
|
||||
Description = reader["Description"]?.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 执行非查询SQL语句
|
||||
/// </summary>
|
||||
@ -2275,14 +2664,16 @@ namespace NavisworksTransport
|
||||
FrameRate, DurationSeconds, DetectionToleranceMeters,
|
||||
IsVirtualObject, VirtualObjectLength, VirtualObjectWidth, VirtualObjectHeight,
|
||||
DetectAllObjects,
|
||||
ClashDetectiveTestName, CollisionCount, ObjectRotationCorrection
|
||||
ClashDetectiveTestName, CollisionCount, ObjectRotationCorrection,
|
||||
DetectionRecordId
|
||||
)
|
||||
VALUES (
|
||||
@RouteId, @Status, @CreatedTime, @StartTime, @EndTime, @ErrorMessage,
|
||||
@FrameRate, @DurationSeconds, @DetectionToleranceMeters,
|
||||
@IsVirtualObject, @VirtualObjectLength, @VirtualObjectWidth, @VirtualObjectHeight,
|
||||
@DetectAllObjects,
|
||||
@ClashDetectiveTestName, @CollisionCount, @ObjectRotationCorrection
|
||||
@ClashDetectiveTestName, @CollisionCount, @ObjectRotationCorrection,
|
||||
@DetectionRecordId
|
||||
);
|
||||
SELECT last_insert_rowid();";
|
||||
|
||||
@ -2303,6 +2694,7 @@ namespace NavisworksTransport
|
||||
cmd.Parameters.AddWithValue("@ClashDetectiveTestName", item.ClashDetectiveTestName ?? "");
|
||||
cmd.Parameters.AddWithValue("@CollisionCount", item.CollisionCount ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@ObjectRotationCorrection", item.ObjectRotationCorrection);
|
||||
cmd.Parameters.AddWithValue("@DetectionRecordId", item.DetectionRecordId ?? (object)DBNull.Value);
|
||||
|
||||
var queueItemId = Convert.ToInt32(cmd.ExecuteScalar());
|
||||
|
||||
@ -2345,7 +2737,8 @@ namespace NavisworksTransport
|
||||
DetectAllObjects = Convert.ToBoolean(reader["DetectAllObjects"]),
|
||||
ClashDetectiveTestName = reader["ClashDetectiveTestName"].ToString(),
|
||||
CollisionCount = !Convert.IsDBNull(reader["CollisionCount"]) ? (int?)Convert.ToInt32(reader["CollisionCount"]) : null,
|
||||
ObjectRotationCorrection = Convert.ToDouble(reader["ObjectRotationCorrection"])
|
||||
ObjectRotationCorrection = Convert.ToDouble(reader["ObjectRotationCorrection"]),
|
||||
DetectionRecordId = !Convert.IsDBNull(reader["DetectionRecordId"]) ? (int?)Convert.ToInt32(reader["DetectionRecordId"]) : null
|
||||
};
|
||||
|
||||
// 填充 MovingObjectName 属性
|
||||
@ -2378,7 +2771,8 @@ namespace NavisworksTransport
|
||||
bqi.FrameRate, bqi.DurationSeconds, bqi.DetectionToleranceMeters,
|
||||
bqi.IsVirtualObject, bqi.VirtualObjectLength, bqi.VirtualObjectWidth, bqi.VirtualObjectHeight,
|
||||
bqi.DetectAllObjects,
|
||||
bqi.ClashDetectiveTestName, bqi.CollisionCount, bqi.ObjectRotationCorrection
|
||||
bqi.ClashDetectiveTestName, bqi.CollisionCount, bqi.ObjectRotationCorrection,
|
||||
bqi.DetectionRecordId
|
||||
FROM BatchQueueItems bqi
|
||||
INNER JOIN PathRoutes pr ON bqi.RouteId = pr.Id";
|
||||
var conditions = new List<string>();
|
||||
@ -2437,7 +2831,8 @@ namespace NavisworksTransport
|
||||
bqi.FrameRate, bqi.DurationSeconds, bqi.DetectionToleranceMeters,
|
||||
bqi.IsVirtualObject, bqi.VirtualObjectLength, bqi.VirtualObjectWidth, bqi.VirtualObjectHeight,
|
||||
bqi.DetectAllObjects,
|
||||
bqi.ClashDetectiveTestName, bqi.CollisionCount, bqi.ObjectRotationCorrection
|
||||
bqi.ClashDetectiveTestName, bqi.CollisionCount, bqi.ObjectRotationCorrection,
|
||||
bqi.DetectionRecordId
|
||||
FROM BatchQueueItems bqi
|
||||
INNER JOIN PathRoutes pr ON bqi.RouteId = pr.Id
|
||||
WHERE bqi.Id = @Id";
|
||||
@ -3030,6 +3425,7 @@ namespace NavisworksTransport
|
||||
public double VirtualObjectWidth { get; set; }
|
||||
public double VirtualObjectHeight { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public int? DetectionRecordId { get; set; } // 关联到检测记录
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -3053,6 +3449,73 @@ namespace NavisworksTransport
|
||||
public bool HasPositionInfo { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 碰撞检测记录
|
||||
/// 每次点击"生成动画"创建一条记录,保存当时的完整配置
|
||||
/// </summary>
|
||||
public class CollisionDetectionRecord
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string RouteId { get; set; }
|
||||
public DateTime CreatedTime { get; set; }
|
||||
|
||||
// 动画参数
|
||||
public int FrameRate { get; set; }
|
||||
public double DurationSeconds { get; set; }
|
||||
|
||||
// 碰撞检测参数
|
||||
public double DetectionToleranceMeters { get; set; }
|
||||
|
||||
// 运动物体参数
|
||||
public bool IsVirtualObject { get; set; }
|
||||
public string AnimatedObjectName { get; set; }
|
||||
public int? ObjectModelIndex { get; set; }
|
||||
public string ObjectPathId { get; set; }
|
||||
public double? VirtualObjectLength { get; set; }
|
||||
public double? VirtualObjectWidth { get; set; }
|
||||
public double? VirtualObjectHeight { get; set; }
|
||||
|
||||
// 检测模式
|
||||
public bool DetectAllObjects { get; set; }
|
||||
|
||||
// 角度修正
|
||||
public double ObjectRotationCorrection { get; set; }
|
||||
|
||||
// 备注
|
||||
public string Description { get; set; }
|
||||
|
||||
// 关联数据(不存入数据库,查询时填充)
|
||||
public List<CollisionDetectionExcludedObjectRecord> ExcludedObjects { get; set; }
|
||||
public List<CollisionDetectionManualTargetRecord> ManualTargets { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 碰撞检测记录排除对象
|
||||
/// </summary>
|
||||
public class CollisionDetectionExcludedObjectRecord
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int DetectionRecordId { get; set; }
|
||||
public int ModelIndex { get; set; }
|
||||
public string PathId { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public string ObjectName { get; set; }
|
||||
public string Reason { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 碰撞检测记录手工目标
|
||||
/// </summary>
|
||||
public class CollisionDetectionManualTargetRecord
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int DetectionRecordId { get; set; }
|
||||
public int ModelIndex { get; set; }
|
||||
public string PathId { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public string ObjectName { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 碰撞报告截图记录
|
||||
/// </summary>
|
||||
|
||||
@ -2023,8 +2023,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 获取路由ID
|
||||
string routeId = CurrentPathRoute?.Id ?? "";
|
||||
|
||||
// 先保存排除对象到数据库(确保排除对象有记录ID)
|
||||
_pathAnimationManager?.SaveExcludedObjectsToDatabase(pathDatabase, routeId);
|
||||
// 注意:检测记录(包括排除列表)已在生成动画时保存到 CollisionDetectionRecords 表
|
||||
// 这里只保存碰撞报告,不重复保存排除列表
|
||||
|
||||
// 从报告中获取所有需要的数据
|
||||
await Task.Run(() =>
|
||||
@ -2979,6 +2979,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 用户选择直接继续(无排除对象)
|
||||
LogManager.Info("[碰撞分析] 用户选择直接继续生成");
|
||||
UpdateMainStatus($"预计算碰撞分析完成,共 {totalCollisions} 个候选碰撞");
|
||||
|
||||
// 🔥 保存检测记录到数据库(唯一保存位置:生成动画完成后)
|
||||
SaveCollisionDetectionRecord();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -2987,6 +2990,142 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存检测记录到数据库
|
||||
/// 在生成动画完成后调用,创建检测记录并保存当时的完整配置
|
||||
/// </summary>
|
||||
private int? SaveCollisionDetectionRecord()
|
||||
{
|
||||
try
|
||||
{
|
||||
var pathDatabase = _pathPlanningManager?.GetPathDatabase();
|
||||
if (pathDatabase == null)
|
||||
{
|
||||
LogManager.Warning("[检测记录] 数据库未初始化,无法保存检测记录");
|
||||
return null;
|
||||
}
|
||||
|
||||
string routeId = CurrentPathRoute?.Id ?? "";
|
||||
|
||||
// 创建检测记录
|
||||
var record = new CollisionDetectionRecord
|
||||
{
|
||||
RouteId = routeId,
|
||||
CreatedTime = DateTime.Now,
|
||||
FrameRate = _animationFrameRate,
|
||||
DurationSeconds = AnimationDuration,
|
||||
DetectionToleranceMeters = _detectionTolerance,
|
||||
IsVirtualObject = UseVirtualObject,
|
||||
AnimatedObjectName = UseVirtualObject ? "虚拟物体" : SelectedAnimatedObject?.DisplayName,
|
||||
DetectAllObjects = !IsManualCollisionTargetEnabled,
|
||||
ObjectRotationCorrection = _objectRotationCorrection,
|
||||
Description = $"动画生成于 {DateTime.Now:yyyy-MM-dd HH:mm:ss}"
|
||||
};
|
||||
|
||||
// 虚拟物体尺寸
|
||||
if (UseVirtualObject)
|
||||
{
|
||||
record.VirtualObjectLength = VirtualObjectLength;
|
||||
record.VirtualObjectWidth = VirtualObjectWidth;
|
||||
record.VirtualObjectHeight = VirtualObjectHeight;
|
||||
}
|
||||
// 真实物体信息
|
||||
else if (SelectedAnimatedObject != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pathId = Autodesk.Navisworks.Api.Application.ActiveDocument.Models.CreatePathId(SelectedAnimatedObject);
|
||||
record.ObjectModelIndex = pathId.ModelIndex;
|
||||
record.ObjectPathId = pathId.PathId;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[检测记录] 获取运动物体PathId失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 保存检测记录
|
||||
int recordId = pathDatabase.SaveCollisionDetectionRecord(record);
|
||||
LogManager.Info($"[检测记录] 已创建记录 (Id={recordId})");
|
||||
|
||||
// 🔥 设置当前检测记录ID到PathAnimationManager(供碰撞结果保存时关联使用)
|
||||
_pathAnimationManager.CurrentDetectionRecordId = recordId;
|
||||
|
||||
// 保存排除列表
|
||||
var excludedObjects = _pathAnimationManager?.GetExcludedObjects();
|
||||
if (excludedObjects != null && excludedObjects.Count > 0)
|
||||
{
|
||||
var excludedRecords = new List<CollisionDetectionExcludedObjectRecord>();
|
||||
foreach (var obj in excludedObjects)
|
||||
{
|
||||
if (obj == null) continue;
|
||||
try
|
||||
{
|
||||
var pathId = Autodesk.Navisworks.Api.Application.ActiveDocument.Models.CreatePathId(obj);
|
||||
excludedRecords.Add(new CollisionDetectionExcludedObjectRecord
|
||||
{
|
||||
DetectionRecordId = recordId,
|
||||
ModelIndex = pathId.ModelIndex,
|
||||
PathId = pathId.PathId,
|
||||
DisplayName = obj.DisplayName,
|
||||
ObjectName = obj.DisplayName,
|
||||
Reason = "预计算碰撞分析排除"
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[检测记录] 准备排除对象记录失败: {obj.DisplayName}, {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
if (excludedRecords.Count > 0)
|
||||
{
|
||||
pathDatabase.SaveCollisionDetectionExcludedObjects(recordId, excludedRecords);
|
||||
LogManager.Info($"[检测记录] 已保存 {excludedRecords.Count} 个排除对象");
|
||||
}
|
||||
}
|
||||
|
||||
// 保存手工目标
|
||||
if (IsManualCollisionTargetEnabled && _manualCollisionTargets != null && _manualCollisionTargets.Count > 0)
|
||||
{
|
||||
var manualTargetRecords = new List<CollisionDetectionManualTargetRecord>();
|
||||
foreach (var target in _manualCollisionTargets)
|
||||
{
|
||||
if (target?.ModelItem == null) continue;
|
||||
try
|
||||
{
|
||||
var pathId = Autodesk.Navisworks.Api.Application.ActiveDocument.Models.CreatePathId(target.ModelItem);
|
||||
manualTargetRecords.Add(new CollisionDetectionManualTargetRecord
|
||||
{
|
||||
DetectionRecordId = recordId,
|
||||
ModelIndex = pathId.ModelIndex,
|
||||
PathId = pathId.PathId,
|
||||
DisplayName = target.ModelItem.DisplayName,
|
||||
ObjectName = target.ModelItem.DisplayName
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[检测记录] 准备手工目标记录失败: {target.DisplayName}, {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
if (manualTargetRecords.Count > 0)
|
||||
{
|
||||
pathDatabase.SaveCollisionDetectionManualTargets(recordId, manualTargetRecords);
|
||||
LogManager.Info($"[检测记录] 已保存 {manualTargetRecords.Count} 个手工目标");
|
||||
}
|
||||
}
|
||||
|
||||
return recordId;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[检测记录] 保存失败: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteToggleWireframeMode()
|
||||
{
|
||||
try
|
||||
@ -4568,8 +4707,49 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
// 🔥 检查排除列表是否已设置
|
||||
var excludedObjects = _pathAnimationManager?.GetExcludedObjects();
|
||||
if (excludedObjects == null || excludedObjects.Count == 0)
|
||||
{
|
||||
LogManager.Warning("[批处理] 排除列表为空,提示用户");
|
||||
var result = System.Windows.MessageBox.Show(
|
||||
"排除列表为空。对于复杂路径,建议先运行\"生成动画\"进行预计算碰撞分析,设置排除列表后再添加到批处理。\n\n" +
|
||||
"• 是:继续添加(不推荐,可能检测时间过长)\n" +
|
||||
"• 否:取消添加,先去设置排除列表\n\n" +
|
||||
"提示:排除列表可以显著减少复杂路径的碰撞检测时间。",
|
||||
"添加到批处理确认",
|
||||
System.Windows.MessageBoxButton.YesNo,
|
||||
System.Windows.MessageBoxImage.Warning
|
||||
);
|
||||
|
||||
if (result == System.Windows.MessageBoxResult.No)
|
||||
{
|
||||
LogManager.Info("[批处理] 用户选择取消添加,去设置排除列表");
|
||||
return;
|
||||
}
|
||||
|
||||
LogManager.Info("[批处理] 用户选择继续添加(不使用排除列表)");
|
||||
}
|
||||
|
||||
LogManager.Info($"[批处理] 添加路径到批处理队列: {CurrentPathRoute.Name}");
|
||||
|
||||
// 🔥 获取该路径最新的检测记录ID
|
||||
int? detectionRecordId = null;
|
||||
var pathDatabase = _pathPlanningManager?.GetPathDatabase();
|
||||
if (pathDatabase != null)
|
||||
{
|
||||
var latestRecord = pathDatabase.GetLatestCollisionDetectionRecord(CurrentPathRoute.Id);
|
||||
detectionRecordId = latestRecord?.Id;
|
||||
if (detectionRecordId.HasValue)
|
||||
{
|
||||
LogManager.Info($"[批处理] 关联检测记录 (Id={detectionRecordId})");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Warning("[批处理] 未找到检测记录,请先运行\"生成动画\"");
|
||||
}
|
||||
}
|
||||
|
||||
// 创建批处理队列项
|
||||
var queueItem = new BatchQueueItem
|
||||
{
|
||||
@ -4594,7 +4774,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
DetectAllObjects = !IsManualCollisionTargetEnabled,
|
||||
|
||||
// 角度修正配置
|
||||
ObjectRotationCorrection = _objectRotationCorrection
|
||||
ObjectRotationCorrection = _objectRotationCorrection,
|
||||
|
||||
// 🔥 关联的检测记录ID
|
||||
DetectionRecordId = detectionRecordId
|
||||
};
|
||||
|
||||
// 调用批处理管理器添加到队列
|
||||
@ -4669,6 +4852,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
// 🔥 注意:排除列表和手工目标已通过 DetectionRecordId 关联到 CollisionDetectionRecords 表
|
||||
// 不需要再保存到 ModelItemReferences 表
|
||||
|
||||
LogManager.Info($"[批处理] 已创建队列项: {CurrentPathRoute.Name}, " +
|
||||
$"虚拟物体: {queueItem.IsVirtualObject}, " +
|
||||
$"帧率: {queueItem.FrameRate}, " +
|
||||
|
||||
@ -96,20 +96,16 @@
|
||||
<!-- 热点列表 -->
|
||||
<Border Grid.Row="3" BorderBrush="{StaticResource NavisworksLightBrush}"
|
||||
BorderThickness="1" CornerRadius="3" Margin="0,0,0,12">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled">
|
||||
<DataGrid x:Name="HotspotsDataGrid"
|
||||
AutoGenerateColumns="False"
|
||||
CanUserAddRows="False"
|
||||
SelectionMode="Single"
|
||||
GridLinesVisibility="Horizontal"
|
||||
BorderThickness="0"
|
||||
HeadersVisibility="Column"
|
||||
RowBackground="White"
|
||||
AlternatingRowBackground="{StaticResource NavisworksBackgroundBrush}"
|
||||
MinHeight="350"
|
||||
MaxHeight="450"
|
||||
SelectionChanged="HotspotsDataGrid_SelectionChanged">
|
||||
<DataGrid x:Name="HotspotsDataGrid"
|
||||
AutoGenerateColumns="False"
|
||||
CanUserAddRows="False"
|
||||
SelectionMode="Single"
|
||||
GridLinesVisibility="Horizontal"
|
||||
BorderThickness="0"
|
||||
HeadersVisibility="Column"
|
||||
RowBackground="White"
|
||||
AlternatingRowBackground="{StaticResource NavisworksBackgroundBrush}"
|
||||
SelectionChanged="HotspotsDataGrid_SelectionChanged">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="序号" Width="45"
|
||||
Binding="{Binding Index}" IsReadOnly="True">
|
||||
@ -160,8 +156,7 @@
|
||||
</DataGridTextColumn.ElementStyle>
|
||||
</DataGridTextColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</ScrollViewer>
|
||||
</DataGrid>
|
||||
</Border>
|
||||
|
||||
<!-- 选中物体详情 -->
|
||||
|
||||
Loading…
Reference in New Issue
Block a user