1895 lines
92 KiB
C#
1895 lines
92 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Data.SQLite;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using Autodesk.Navisworks.Api;
|
||
using NavisworksTransport.Core.Config;
|
||
using NavisworksTransport.Core.Models;
|
||
|
||
namespace NavisworksTransport
|
||
{
|
||
/// <summary>
|
||
/// 路径数据库管理器 - 简化版
|
||
/// 负责路径和碰撞数据的持久化存储
|
||
/// </summary>
|
||
public class PathDatabase : IDisposable
|
||
{
|
||
internal SQLiteConnection _connection;
|
||
private readonly string _dbPath;
|
||
|
||
/// <summary>
|
||
/// 数据库连接(供内部使用)
|
||
/// </summary>
|
||
internal SQLiteConnection Connection => _connection;
|
||
|
||
/// <summary>
|
||
/// 初始化路径数据库
|
||
/// </summary>
|
||
/// <param name="documentPath">Navisworks文档路径</param>
|
||
public PathDatabase(string documentPath)
|
||
{
|
||
// 数据库文件与nwd文件同名,扩展名为.db
|
||
_dbPath = Path.ChangeExtension(documentPath, ".db");
|
||
|
||
InitializeDatabase();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化数据库连接和表结构
|
||
/// </summary>
|
||
private void InitializeDatabase()
|
||
{
|
||
bool isNewDatabase = !File.Exists(_dbPath);
|
||
|
||
_connection = new SQLiteConnection($"Data Source={_dbPath};Version=3;");
|
||
_connection.Open();
|
||
|
||
// 启用WAL模式(Write-Ahead Logging,支持并发读)
|
||
ExecuteNonQuery("PRAGMA journal_mode=WAL");
|
||
ExecuteNonQuery("PRAGMA foreign_keys=ON");
|
||
|
||
// 无论数据库是否为新数据库,都调用CreateTables()
|
||
// 因为使用了CREATE TABLE IF NOT EXISTS语法,所以是安全的
|
||
// 这样可以确保所有表都存在,避免在旧数据库中缺少新表的问题
|
||
CreateTables();
|
||
|
||
if (isNewDatabase)
|
||
{
|
||
LogManager.Info($"创建新数据库: {_dbPath}");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Info($"打开现有数据库: {_dbPath}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建数据库表结构
|
||
/// </summary>
|
||
private void CreateTables()
|
||
{
|
||
// 1. 路径基本信息表
|
||
ExecuteNonQuery(@"
|
||
CREATE TABLE IF NOT EXISTS PathRoutes (
|
||
Id TEXT PRIMARY KEY,
|
||
Name TEXT NOT NULL,
|
||
TotalLength REAL,
|
||
EstimatedTime REAL,
|
||
TurnRadius REAL,
|
||
IsCurved INTEGER,
|
||
MaxVehicleLength REAL,
|
||
MaxVehicleWidth REAL,
|
||
MaxVehicleHeight REAL,
|
||
SafetyMargin REAL,
|
||
GridSize REAL,
|
||
PathType INTEGER,
|
||
AerialSubType INTEGER,
|
||
LiftHeightMeters REAL,
|
||
CreatedTime DATETIME,
|
||
LastModified DATETIME
|
||
)
|
||
");
|
||
|
||
// 2. 碰撞报告表(替换原有的CollisionResults表)
|
||
ExecuteNonQuery(@"
|
||
CREATE TABLE IF NOT EXISTS CollisionReports (
|
||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
RouteId TEXT,
|
||
PathName TEXT,
|
||
AnimatedObjectName TEXT,
|
||
UniqueCollidedObjectsCount INTEGER,
|
||
FrameRate INTEGER,
|
||
Duration REAL,
|
||
DetectionGap REAL,
|
||
AnimationCollisionCount INTEGER,
|
||
ClashDetectiveCollisionCount INTEGER,
|
||
ReportTime DATETIME
|
||
)
|
||
");
|
||
|
||
// 3. 分析结果表
|
||
ExecuteNonQuery(@"
|
||
CREATE TABLE IF NOT EXISTS AnalysisResults (
|
||
RouteId TEXT PRIMARY KEY,
|
||
CollisionCount INTEGER,
|
||
SafetyScore REAL,
|
||
EfficiencyScore REAL,
|
||
OverallScore REAL,
|
||
AnalysisTime DATETIME,
|
||
Strategy TEXT,
|
||
FOREIGN KEY(RouteId) REFERENCES PathRoutes(Id) ON DELETE CASCADE
|
||
)
|
||
");
|
||
|
||
// 4. 路径点表(新增 CustomTurnRadius)
|
||
ExecuteNonQuery(@"
|
||
CREATE TABLE IF NOT EXISTS PathPoints (
|
||
Id TEXT PRIMARY KEY,
|
||
RouteId TEXT NOT NULL,
|
||
SequenceNumber INTEGER NOT NULL,
|
||
Name TEXT,
|
||
X REAL,
|
||
Y REAL,
|
||
Z REAL,
|
||
Type INTEGER,
|
||
CustomTurnRadius REAL,
|
||
FOREIGN KEY(RouteId) REFERENCES PathRoutes(Id) ON DELETE CASCADE
|
||
)
|
||
");
|
||
|
||
// 5. 路径边表(新增SequenceNumber字段)
|
||
ExecuteNonQuery(@"
|
||
CREATE TABLE IF NOT EXISTS PathEdges (
|
||
Id TEXT PRIMARY KEY,
|
||
RouteId TEXT NOT NULL,
|
||
StartPointId TEXT NOT NULL,
|
||
EndPointId TEXT NOT NULL,
|
||
SegmentType INTEGER NOT NULL,
|
||
PhysicalLength REAL NOT NULL,
|
||
SequenceNumber INTEGER NOT NULL,
|
||
Ts_X REAL,
|
||
Ts_Y REAL,
|
||
Ts_Z REAL,
|
||
Te_X REAL,
|
||
Te_Y REAL,
|
||
Te_Z REAL,
|
||
ArcCenter_X REAL,
|
||
ArcCenter_Y REAL,
|
||
ArcCenter_Z REAL,
|
||
RequestedRadius REAL,
|
||
ActualRadius REAL,
|
||
DeflectionAngle REAL,
|
||
ArcLength REAL,
|
||
FOREIGN KEY(RouteId) REFERENCES PathRoutes(Id) ON DELETE CASCADE
|
||
)
|
||
");
|
||
|
||
// 6. ClashDetective结果表
|
||
ExecuteNonQuery(@"
|
||
CREATE TABLE IF NOT EXISTS ClashDetectiveResults (
|
||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
TestName TEXT NOT NULL UNIQUE,
|
||
PathName TEXT NOT NULL,
|
||
RouteId TEXT NOT NULL,
|
||
TestTime DATETIME NOT NULL,
|
||
CollisionCount INTEGER NOT NULL,
|
||
AnimationCollisionCount INTEGER NOT NULL,
|
||
FrameRate INTEGER,
|
||
Duration REAL,
|
||
DetectionGap REAL,
|
||
AnimatedObjectName TEXT,
|
||
IsVirtualVehicle INTEGER,
|
||
VehicleModelIndex INTEGER,
|
||
VehiclePathId TEXT,
|
||
VirtualVehicleLength REAL,
|
||
VirtualVehicleWidth REAL,
|
||
VirtualVehicleHeight REAL,
|
||
CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
)
|
||
");
|
||
|
||
// 7. ClashDetective碰撞对象表
|
||
ExecuteNonQuery(@"
|
||
CREATE TABLE IF NOT EXISTS ClashDetectiveCollisionObjects (
|
||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
ResultId INTEGER NOT NULL,
|
||
ModelIndex INTEGER,
|
||
PathId TEXT,
|
||
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(PathId)");
|
||
|
||
// 8. 批处理队列表(简化版,只维护FIFO队列)
|
||
ExecuteNonQuery(@"
|
||
CREATE TABLE IF NOT EXISTS BatchQueueItems (
|
||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
PathRouteId TEXT NOT NULL,
|
||
PathRouteName TEXT NOT NULL,
|
||
Status TEXT NOT NULL,
|
||
CreatedTime TEXT NOT NULL,
|
||
StartTime TEXT,
|
||
EndTime TEXT,
|
||
ErrorMessage TEXT,
|
||
FrameRate INTEGER NOT NULL,
|
||
DurationSeconds REAL NOT NULL,
|
||
DetectionToleranceMeters REAL NOT NULL,
|
||
IsVirtualVehicle INTEGER NOT NULL,
|
||
VirtualVehicleLength REAL,
|
||
VirtualVehicleWidth REAL,
|
||
VirtualVehicleHeight REAL,
|
||
VehicleObjectId TEXT,
|
||
VehicleObjectName TEXT,
|
||
DetectionItems TEXT,
|
||
DetectAllObjects INTEGER NOT NULL DEFAULT 1,
|
||
ClashDetectiveTestName TEXT,
|
||
CollisionCount INTEGER
|
||
)
|
||
");
|
||
|
||
// 批处理表索引
|
||
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_batch_queue_status ON BatchQueueItems(Status)");
|
||
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_batch_queue_created ON BatchQueueItems(CreatedTime)");
|
||
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_batch_queue_path ON BatchQueueItems(PathRouteId)");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存路径基本信息
|
||
/// </summary>
|
||
public void SavePathRoute(PathRoute route)
|
||
{
|
||
// 使用事务确保路径和路径点一起保存
|
||
using (var transaction = _connection.BeginTransaction())
|
||
{
|
||
try
|
||
{
|
||
// 保存路径基本信息
|
||
var sql = @"
|
||
INSERT OR REPLACE INTO PathRoutes
|
||
(Id, Name, TotalLength, EstimatedTime, TurnRadius, IsCurved, MaxVehicleLength, MaxVehicleWidth, MaxVehicleHeight, SafetyMargin, GridSize, PathType, AerialSubType, LiftHeightMeters, CreatedTime, LastModified)
|
||
VALUES (@id, @name, @length, @time, @turnRadius, @isCurved, @maxLength, @maxWidth, @maxHeight, @safetyMargin, @gridSize, @pathType, @aerialSubType, @liftHeightMeters, @created, @modified)
|
||
";
|
||
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@id", route.Id);
|
||
cmd.Parameters.AddWithValue("@name", route.Name);
|
||
cmd.Parameters.AddWithValue("@length", route.TotalLength);
|
||
cmd.Parameters.AddWithValue("@time", route.EstimatedTime);
|
||
cmd.Parameters.AddWithValue("@turnRadius", route.TurnRadius);
|
||
cmd.Parameters.AddWithValue("@isCurved", route.IsCurved ? 1 : 0);
|
||
cmd.Parameters.AddWithValue("@maxLength", route.MaxVehicleLength);
|
||
cmd.Parameters.AddWithValue("@maxWidth", route.MaxVehicleWidth);
|
||
cmd.Parameters.AddWithValue("@maxHeight", route.MaxVehicleHeight);
|
||
cmd.Parameters.AddWithValue("@safetyMargin", route.SafetyMargin);
|
||
cmd.Parameters.AddWithValue("@gridSize", route.GridSize);
|
||
cmd.Parameters.AddWithValue("@pathType", (int)route.PathType);
|
||
cmd.Parameters.AddWithValue("@aerialSubType", (int)route.AerialSubType);
|
||
cmd.Parameters.AddWithValue("@liftHeightMeters", route.LiftHeightMeters);
|
||
cmd.Parameters.AddWithValue("@created", route.CreatedTime);
|
||
cmd.Parameters.AddWithValue("@modified", DateTime.Now);
|
||
cmd.ExecuteNonQuery();
|
||
}
|
||
|
||
// 删除旧的路径点和边
|
||
DeletePathPoints(route.Id);
|
||
DeletePathEdges(route.Id);
|
||
|
||
// 保存新的路径点
|
||
if (route.Points != null && route.Points.Count > 0)
|
||
{
|
||
var pointSql = @"
|
||
INSERT INTO PathPoints
|
||
(Id, RouteId, SequenceNumber, Name, X, Y, Z, Type, CustomTurnRadius)
|
||
VALUES (@id, @routeId, @seq, @name, @x, @y, @z, @type, @customRadius)
|
||
";
|
||
|
||
for (int i = 0; i < route.Points.Count; i++)
|
||
{
|
||
var point = route.Points[i];
|
||
using (var cmd = new SQLiteCommand(pointSql, _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@id", point.Id);
|
||
cmd.Parameters.AddWithValue("@routeId", route.Id);
|
||
cmd.Parameters.AddWithValue("@seq", i);
|
||
cmd.Parameters.AddWithValue("@name", point.Name ?? "");
|
||
cmd.Parameters.AddWithValue("@x", point.Position.X);
|
||
cmd.Parameters.AddWithValue("@y", point.Position.Y);
|
||
cmd.Parameters.AddWithValue("@z", point.Position.Z);
|
||
cmd.Parameters.AddWithValue("@type", (int)point.Type);
|
||
cmd.Parameters.AddWithValue("@customRadius", point.CustomTurnRadius.HasValue ?
|
||
(object)point.CustomTurnRadius.Value : DBNull.Value);
|
||
cmd.ExecuteNonQuery();
|
||
}
|
||
}
|
||
|
||
LogManager.Info($"保存路径: {route.Name},包含 {route.Points.Count} 个路径点");
|
||
}
|
||
|
||
// 保存路径边
|
||
if (route.Edges != null && route.Edges.Count > 0)
|
||
{
|
||
for (int i = 0; i < route.Edges.Count; i++)
|
||
{
|
||
SavePathEdge(route.Id, route.Edges[i], i);
|
||
}
|
||
|
||
LogManager.Info($"保存路径: {route.Name},包含 {route.Edges.Count} 个路径边");
|
||
}
|
||
|
||
transaction.Commit();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
transaction.Rollback();
|
||
LogManager.Error($"保存路径失败: {ex.Message}", ex);
|
||
throw;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存碰撞报告
|
||
/// </summary>
|
||
public void SaveCollisionReport(string routeId, string pathName, string animatedObjectName,
|
||
int uniqueCollidedObjectsCount, int frameRate, double duration, double detectionGap,
|
||
int animationCollisionCount, int clashDetectiveCollisionCount)
|
||
{
|
||
try
|
||
{
|
||
var sql = @"
|
||
INSERT INTO CollisionReports
|
||
(RouteId, PathName, AnimatedObjectName, UniqueCollidedObjectsCount, FrameRate, Duration,
|
||
DetectionGap, AnimationCollisionCount, ClashDetectiveCollisionCount, ReportTime)
|
||
VALUES (@routeId, @pathName, @animatedObjectName, @uniqueCollidedObjectsCount, @frameRate, @duration,
|
||
@detectionGap, @animationCount, @clashCount, @reportTime)
|
||
";
|
||
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@routeId", routeId ?? "");
|
||
cmd.Parameters.AddWithValue("@pathName", pathName ?? "未知路径");
|
||
cmd.Parameters.AddWithValue("@animatedObjectName", animatedObjectName ?? "未知对象");
|
||
cmd.Parameters.AddWithValue("@uniqueCollidedObjectsCount", uniqueCollidedObjectsCount);
|
||
cmd.Parameters.AddWithValue("@frameRate", frameRate);
|
||
cmd.Parameters.AddWithValue("@duration", duration);
|
||
cmd.Parameters.AddWithValue("@detectionGap", detectionGap);
|
||
cmd.Parameters.AddWithValue("@animationCount", animationCollisionCount);
|
||
cmd.Parameters.AddWithValue("@clashCount", clashDetectiveCollisionCount);
|
||
cmd.Parameters.AddWithValue("@reportTime", DateTime.Now);
|
||
cmd.ExecuteNonQuery();
|
||
}
|
||
|
||
LogManager.Info($"碰撞报告已保存: 路径={pathName}, 碰撞构件={uniqueCollidedObjectsCount}, 动画碰撞={animationCollisionCount}, ClashDetective碰撞={clashDetectiveCollisionCount}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"保存碰撞报告失败: {ex.Message}", ex);
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存路径的碰撞结果(已废弃,保留以兼容旧代码)
|
||
/// </summary>
|
||
public void SaveCollisions(string routeId, List<CollisionResult> collisions)
|
||
{
|
||
// 先删除旧的碰撞数据
|
||
ExecuteNonQuery("DELETE FROM CollisionResults WHERE RouteId=@id",
|
||
new { id = routeId });
|
||
|
||
if (collisions == null || collisions.Count == 0)
|
||
return;
|
||
|
||
// 批量插入新数据
|
||
using (var transaction = _connection.BeginTransaction())
|
||
{
|
||
try
|
||
{
|
||
var sql = @"
|
||
INSERT INTO CollisionResults
|
||
(RouteId, Item1Name, Item2Name, CenterX, CenterY, CenterZ, Distance, DetectedTime)
|
||
VALUES (@rid, @i1, @i2, @x, @y, @z, @dist, @time)
|
||
";
|
||
|
||
foreach (var collision in collisions)
|
||
{
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@rid", routeId);
|
||
cmd.Parameters.AddWithValue("@i1", GetItemDisplayName(collision.Item1));
|
||
cmd.Parameters.AddWithValue("@i2", GetItemDisplayName(collision.Item2));
|
||
cmd.Parameters.AddWithValue("@x", collision.Center.X);
|
||
cmd.Parameters.AddWithValue("@y", collision.Center.Y);
|
||
cmd.Parameters.AddWithValue("@z", collision.Center.Z);
|
||
cmd.Parameters.AddWithValue("@dist", collision.Distance);
|
||
cmd.Parameters.AddWithValue("@time", DateTime.Now);
|
||
cmd.ExecuteNonQuery();
|
||
}
|
||
}
|
||
|
||
transaction.Commit();
|
||
LogManager.Info($"保存碰撞结果: RouteId={routeId}, Count={collisions.Count}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
transaction.Rollback();
|
||
LogManager.Error($"保存碰撞结果失败: {ex.Message}", ex);
|
||
throw;
|
||
}
|
||
}
|
||
}
|
||
|
||
#region ClashDetective结果管理
|
||
|
||
/// <summary>
|
||
/// 保存ClashDetective碰撞检测结果
|
||
/// </summary>
|
||
public int SaveClashDetectiveResult(ClashDetectiveResultRecord record)
|
||
{
|
||
try
|
||
{
|
||
// 检查TestName是否已存在
|
||
var checkSql = "SELECT COUNT(*) FROM ClashDetectiveResults WHERE TestName = @testName";
|
||
using (var checkCmd = new SQLiteCommand(checkSql, _connection))
|
||
{
|
||
checkCmd.Parameters.AddWithValue("@testName", record.TestName);
|
||
var count = Convert.ToInt32(checkCmd.ExecuteScalar());
|
||
if (count > 0)
|
||
{
|
||
throw new InvalidOperationException($"测试名称 '{record.TestName}' 已存在,无法重复保存");
|
||
}
|
||
}
|
||
|
||
var sql = @"
|
||
INSERT INTO ClashDetectiveResults
|
||
(TestName, PathName, RouteId, TestTime, CollisionCount, AnimationCollisionCount,
|
||
FrameRate, Duration, DetectionGap, AnimatedObjectName, IsVirtualVehicle, VehicleModelIndex, VehiclePathId,
|
||
VirtualVehicleLength, VirtualVehicleWidth, VirtualVehicleHeight, CreatedAt)
|
||
VALUES (@testName, @pathName, @routeId, @testTime, @collisionCount, @animationCollisionCount,
|
||
@frameRate, @duration, @detectionGap, @animatedObjectName, @isVirtualVehicle, @vehicleModelIndex, @vehiclePathId,
|
||
@virtualVehicleLength, @virtualVehicleWidth, @virtualVehicleHeight, @createdAt)
|
||
";
|
||
|
||
long newId = 0;
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@testName", record.TestName);
|
||
cmd.Parameters.AddWithValue("@pathName", record.PathName);
|
||
cmd.Parameters.AddWithValue("@routeId", record.RouteId);
|
||
cmd.Parameters.AddWithValue("@testTime", record.TestTime);
|
||
cmd.Parameters.AddWithValue("@collisionCount", record.CollisionCount);
|
||
cmd.Parameters.AddWithValue("@animationCollisionCount", record.AnimationCollisionCount);
|
||
cmd.Parameters.AddWithValue("@frameRate", record.FrameRate);
|
||
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("@vehicleModelIndex", record.VehicleModelIndex.HasValue ? (object)record.VehicleModelIndex.Value : DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@vehiclePathId", record.VehiclePathId ?? (object)DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@virtualVehicleLength", record.VirtualVehicleLength);
|
||
cmd.Parameters.AddWithValue("@virtualVehicleWidth", record.VirtualVehicleWidth);
|
||
cmd.Parameters.AddWithValue("@virtualVehicleHeight", record.VirtualVehicleHeight);
|
||
cmd.Parameters.AddWithValue("@createdAt", record.CreatedAt);
|
||
cmd.ExecuteNonQuery();
|
||
newId = _connection.LastInsertRowId;
|
||
}
|
||
|
||
LogManager.Info($"ClashDetective结果已保存: 测试={record.TestName}, 碰撞数={record.CollisionCount}, Id={newId}");
|
||
return (int)newId;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"保存ClashDetective结果失败: {ex.Message}", ex);
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取路径的所有ClashDetective检测结果
|
||
/// </summary>
|
||
public List<ClashDetectiveResultRecord> GetClashDetectiveResultsByPath(string pathName)
|
||
{
|
||
var results = new List<ClashDetectiveResultRecord>();
|
||
|
||
try
|
||
{
|
||
var sql = @"
|
||
SELECT Id, TestName, PathName, RouteId, TestTime, CollisionCount, AnimationCollisionCount,
|
||
FrameRate, Duration, DetectionGap, AnimatedObjectName, CreatedAt
|
||
FROM ClashDetectiveResults
|
||
WHERE PathName = @pathName
|
||
ORDER BY TestTime DESC
|
||
";
|
||
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@pathName", pathName);
|
||
using (var reader = cmd.ExecuteReader())
|
||
{
|
||
while (reader.Read())
|
||
{
|
||
results.Add(new ClashDetectiveResultRecord
|
||
{
|
||
Id = Convert.ToInt32(reader["Id"]),
|
||
TestName = reader["TestName"].ToString(),
|
||
PathName = reader["PathName"].ToString(),
|
||
RouteId = reader["RouteId"]?.ToString(),
|
||
TestTime = Convert.ToDateTime(reader["TestTime"]),
|
||
CollisionCount = Convert.ToInt32(reader["CollisionCount"]),
|
||
AnimationCollisionCount = Convert.ToInt32(reader["AnimationCollisionCount"]),
|
||
FrameRate = reader["FrameRate"] != DBNull.Value ? Convert.ToInt32(reader["FrameRate"]) : 0,
|
||
Duration = reader["Duration"] != DBNull.Value ? Convert.ToDouble(reader["Duration"]) : 0.0,
|
||
DetectionGap = reader["DetectionGap"] != DBNull.Value ? Convert.ToDouble(reader["DetectionGap"]) : 0.0,
|
||
AnimatedObjectName = reader["AnimatedObjectName"]?.ToString(),
|
||
CreatedAt = Convert.ToDateTime(reader["CreatedAt"])
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
LogManager.Debug($"获取到 {results.Count} 条ClashDetective结果记录");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"获取ClashDetective结果失败: {ex.Message}", ex);
|
||
}
|
||
|
||
return results;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取路径最新的ClashDetective检测结果
|
||
/// </summary>
|
||
public ClashDetectiveResultRecord GetLatestClashDetectiveResult(string pathName)
|
||
{
|
||
try
|
||
{
|
||
var sql = @"
|
||
SELECT Id, TestName, PathName, RouteId, TestTime, CollisionCount, AnimationCollisionCount,
|
||
FrameRate, Duration, DetectionGap, AnimatedObjectName, CreatedAt
|
||
FROM ClashDetectiveResults
|
||
WHERE PathName = @pathName
|
||
ORDER BY TestTime DESC
|
||
LIMIT 1
|
||
";
|
||
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@pathName", pathName);
|
||
using (var reader = cmd.ExecuteReader())
|
||
{
|
||
if (reader.Read())
|
||
{
|
||
return new ClashDetectiveResultRecord
|
||
{
|
||
Id = Convert.ToInt32(reader["Id"]),
|
||
TestName = reader["TestName"].ToString(),
|
||
PathName = reader["PathName"].ToString(),
|
||
RouteId = reader["RouteId"]?.ToString(),
|
||
TestTime = Convert.ToDateTime(reader["TestTime"]),
|
||
CollisionCount = Convert.ToInt32(reader["CollisionCount"]),
|
||
AnimationCollisionCount = Convert.ToInt32(reader["AnimationCollisionCount"]),
|
||
FrameRate = reader["FrameRate"] != DBNull.Value ? Convert.ToInt32(reader["FrameRate"]) : 0,
|
||
Duration = reader["Duration"] != DBNull.Value ? Convert.ToDouble(reader["Duration"]) : 0.0,
|
||
DetectionGap = reader["DetectionGap"] != DBNull.Value ? Convert.ToDouble(reader["DetectionGap"]) : 0.0,
|
||
AnimatedObjectName = reader["AnimatedObjectName"]?.ToString(),
|
||
CreatedAt = Convert.ToDateTime(reader["CreatedAt"])
|
||
};
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"获取最新ClashDetective结果失败: {ex.Message}", ex);
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除ClashDetective检测结果
|
||
/// </summary>
|
||
public void DeleteClashDetectiveResult(int id)
|
||
{
|
||
try
|
||
{
|
||
ExecuteNonQuery("DELETE FROM ClashDetectiveResults WHERE Id=@id",
|
||
new { id = id });
|
||
LogManager.Info($"已删除ClashDetective结果记录: Id={id}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"删除ClashDetective结果失败: {ex.Message}", ex);
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存ClashDetective碰撞对象
|
||
/// </summary>
|
||
public void SaveClashDetectiveCollisionObjects(int resultId, List<ClashDetectiveCollisionObjectRecord> objects)
|
||
{
|
||
if (objects == null || objects.Count == 0)
|
||
return;
|
||
|
||
try
|
||
{
|
||
using (var transaction = _connection.BeginTransaction())
|
||
{
|
||
var sql = @"
|
||
INSERT INTO ClashDetectiveCollisionObjects
|
||
(ResultId, ModelIndex, PathId, DisplayName, ObjectName)
|
||
VALUES (@resultId, @modelIndex, @pathId, @displayName, @objectName)
|
||
";
|
||
|
||
foreach (var obj in objects)
|
||
{
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@resultId", resultId);
|
||
cmd.Parameters.AddWithValue("@modelIndex", obj.ModelIndex.HasValue ? (object)obj.ModelIndex.Value : DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@pathId", obj.PathId ?? (object)DBNull.Value);
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定ClashDetective结果的所有碰撞对象
|
||
/// </summary>
|
||
public List<ClashDetectiveCollisionObjectRecord> GetClashDetectiveCollisionObjects(int resultId)
|
||
{
|
||
var results = new List<ClashDetectiveCollisionObjectRecord>();
|
||
|
||
try
|
||
{
|
||
var sql = @"
|
||
SELECT Id, ResultId, ModelIndex, PathId, 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"]),
|
||
ModelIndex = reader["ModelIndex"] != DBNull.Value ? Convert.ToInt32(reader["ModelIndex"]) : (int?)null,
|
||
PathId = reader["PathId"] != DBNull.Value ? reader["PathId"].ToString() : null,
|
||
DisplayName = reader["DisplayName"]?.ToString(),
|
||
ObjectName = reader["ObjectName"]?.ToString()
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
LogManager.Debug($"获取到 {results.Count} 个碰撞对象记录");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"获取ClashDetective碰撞对象失败: {ex.Message}", ex);
|
||
}
|
||
|
||
return results;
|
||
}
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 保存分析评分结果
|
||
/// </summary>
|
||
public void SaveAnalysisScore(string routeId, int collisionCount, double safetyScore,
|
||
double efficiencyScore, string strategy)
|
||
{
|
||
var overallScore = (safetyScore + efficiencyScore) / 2.0;
|
||
|
||
var sql = @"
|
||
INSERT OR REPLACE INTO AnalysisResults
|
||
(RouteId, CollisionCount, SafetyScore, EfficiencyScore, OverallScore, AnalysisTime, Strategy)
|
||
VALUES (@id, @count, @safety, @eff, @overall, @time, @strategy)
|
||
";
|
||
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@id", routeId);
|
||
cmd.Parameters.AddWithValue("@count", collisionCount);
|
||
cmd.Parameters.AddWithValue("@safety", safetyScore);
|
||
cmd.Parameters.AddWithValue("@eff", efficiencyScore);
|
||
cmd.Parameters.AddWithValue("@overall", overallScore);
|
||
cmd.Parameters.AddWithValue("@time", DateTime.Now);
|
||
cmd.Parameters.AddWithValue("@strategy", strategy ?? "默认");
|
||
cmd.ExecuteNonQuery();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取路径的碰撞数量
|
||
/// </summary>
|
||
public int GetCollisionCount(string routeId)
|
||
{
|
||
var sql = "SELECT COUNT(*) FROM CollisionResults WHERE RouteId=@id";
|
||
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@id", routeId);
|
||
var result = cmd.ExecuteScalar();
|
||
return Convert.ToInt32(result);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取路径的分析结果
|
||
/// </summary>
|
||
public PathAnalysisResult GetAnalysisResult(string routeId)
|
||
{
|
||
var sql = "SELECT * FROM AnalysisResults WHERE RouteId=@id";
|
||
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@id", routeId);
|
||
using (var reader = cmd.ExecuteReader())
|
||
{
|
||
if (reader.Read())
|
||
{
|
||
return new PathAnalysisResult
|
||
{
|
||
RouteId = reader["RouteId"].ToString(),
|
||
CollisionCount = Convert.ToInt32(reader["CollisionCount"]),
|
||
SafetyScore = Convert.ToDouble(reader["SafetyScore"]),
|
||
EfficiencyScore = Convert.ToDouble(reader["EfficiencyScore"]),
|
||
OverallScore = Convert.ToDouble(reader["OverallScore"]),
|
||
AnalysisTime = Convert.ToDateTime(reader["AnalysisTime"]),
|
||
Strategy = reader["Strategy"].ToString()
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取所有路径的分析结果
|
||
/// </summary>
|
||
public List<PathAnalysisResult> GetAllAnalysisResults()
|
||
{
|
||
var results = new List<PathAnalysisResult>();
|
||
var sql = "SELECT * FROM AnalysisResults ORDER BY OverallScore DESC";
|
||
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
using (var reader = cmd.ExecuteReader())
|
||
{
|
||
while (reader.Read())
|
||
{
|
||
results.Add(new PathAnalysisResult
|
||
{
|
||
RouteId = reader["RouteId"].ToString(),
|
||
CollisionCount = Convert.ToInt32(reader["CollisionCount"]),
|
||
SafetyScore = Convert.ToDouble(reader["SafetyScore"]),
|
||
EfficiencyScore = Convert.ToDouble(reader["EfficiencyScore"]),
|
||
OverallScore = Convert.ToDouble(reader["OverallScore"]),
|
||
AnalysisTime = Convert.ToDateTime(reader["AnalysisTime"]),
|
||
Strategy = reader["Strategy"].ToString()
|
||
});
|
||
}
|
||
}
|
||
|
||
return results;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除路径及其相关数据
|
||
/// </summary>
|
||
/// <param name="routeId">路径ID</param>
|
||
public void DeletePathRoute(string routeId)
|
||
{
|
||
try
|
||
{
|
||
// 由于设置了外键级联删除,删除路径时会自动删除相关的碰撞结果和分析结果
|
||
var sql = "DELETE FROM PathRoutes WHERE Id = @id";
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@id", routeId);
|
||
int affected = cmd.ExecuteNonQuery();
|
||
LogManager.Info($"删除路径记录: ID={routeId}, 影响行数={affected}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"删除路径失败: ID={routeId}, {ex.Message}", ex);
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取所有路径基本信息
|
||
/// </summary>
|
||
public List<PathRoute> GetAllPathRoutes()
|
||
{
|
||
var routes = new List<PathRoute>();
|
||
var sql = @"SELECT pr.*, ar.CollisionCount, ar.SafetyScore, ar.EfficiencyScore, ar.OverallScore
|
||
FROM PathRoutes pr
|
||
LEFT JOIN AnalysisResults ar ON pr.Id = ar.RouteId
|
||
ORDER BY pr.LastModified DESC";
|
||
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
using (var reader = cmd.ExecuteReader())
|
||
{
|
||
while (reader.Read())
|
||
{
|
||
var route = new PathRoute
|
||
{
|
||
Id = reader["Id"].ToString(),
|
||
Name = reader["Name"].ToString(),
|
||
TotalLength = Convert.ToDouble(reader["TotalLength"]),
|
||
EstimatedTime = Convert.ToDouble(reader["EstimatedTime"]),
|
||
TurnRadius = reader.IsDBNull(reader.GetOrdinal("TurnRadius")) ? 0.0 : Convert.ToDouble(reader["TurnRadius"]), // 0.0 表示未设置,实际使用时从配置获取
|
||
IsCurved = reader.IsDBNull(reader.GetOrdinal("IsCurved")) ? false : Convert.ToInt32(reader["IsCurved"]) == 1,
|
||
MaxVehicleLength = reader.IsDBNull(reader.GetOrdinal("MaxVehicleLength")) ? 1.0 : Convert.ToDouble(reader["MaxVehicleLength"]),
|
||
MaxVehicleWidth = reader.IsDBNull(reader.GetOrdinal("MaxVehicleWidth")) ? 1.0 : Convert.ToDouble(reader["MaxVehicleWidth"]),
|
||
MaxVehicleHeight = reader.IsDBNull(reader.GetOrdinal("MaxVehicleHeight")) ? 2.0 : Convert.ToDouble(reader["MaxVehicleHeight"]),
|
||
SafetyMargin = reader.IsDBNull(reader.GetOrdinal("SafetyMargin")) ? 0.5 : Convert.ToDouble(reader["SafetyMargin"]),
|
||
GridSize = reader.IsDBNull(reader.GetOrdinal("GridSize")) ? 0.5 : Convert.ToDouble(reader["GridSize"]),
|
||
PathType = reader.IsDBNull(reader.GetOrdinal("PathType")) ? PathType.Ground : (PathType)Convert.ToInt32(reader["PathType"]),
|
||
AerialSubType = reader.IsDBNull(reader.GetOrdinal("AerialSubType")) ? AerialSubType.Rail : (AerialSubType)Convert.ToInt32(reader["AerialSubType"]),
|
||
LiftHeightMeters = reader.IsDBNull(reader.GetOrdinal("LiftHeightMeters")) ? 0.0 : Convert.ToDouble(reader["LiftHeightMeters"]),
|
||
CreatedTime = Convert.ToDateTime(reader["CreatedTime"]),
|
||
LastModified = Convert.ToDateTime(reader["LastModified"])
|
||
};
|
||
|
||
// 添加分析结果(如果存在)
|
||
if (!reader.IsDBNull(reader.GetOrdinal("CollisionCount")))
|
||
{
|
||
route.CollisionCount = Convert.ToInt32(reader["CollisionCount"]);
|
||
route.SafetyScore = Convert.ToDouble(reader["SafetyScore"]);
|
||
route.EfficiencyScore = Convert.ToDouble(reader["EfficiencyScore"]);
|
||
route.OverallScore = Convert.ToDouble(reader["OverallScore"]);
|
||
}
|
||
|
||
routes.Add(route);
|
||
}
|
||
}
|
||
|
||
// 为每个路径加载路径点和路径边
|
||
foreach (var route in routes)
|
||
{
|
||
LoadPathPoints(route);
|
||
LoadPathEdges(route);
|
||
}
|
||
|
||
LogManager.Info($"从数据库加载了 {routes.Count} 条路径记录");
|
||
return routes;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载指定路径的路径点
|
||
/// </summary>
|
||
private void LoadPathPoints(PathRoute route)
|
||
{
|
||
var sql = @"SELECT * FROM PathPoints
|
||
WHERE RouteId=@routeId
|
||
ORDER BY SequenceNumber";
|
||
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@routeId", route.Id);
|
||
using (var reader = cmd.ExecuteReader())
|
||
{
|
||
int index = 0;
|
||
while (reader.Read())
|
||
{
|
||
var point = new PathPoint
|
||
{
|
||
Id = reader["Id"].ToString(),
|
||
Name = reader["Name"].ToString(),
|
||
Position = new Autodesk.Navisworks.Api.Point3D(
|
||
Convert.ToDouble(reader["X"]),
|
||
Convert.ToDouble(reader["Y"]),
|
||
Convert.ToDouble(reader["Z"])
|
||
),
|
||
Type = (PathPointType)Convert.ToInt32(reader["Type"]),
|
||
Index = index++
|
||
};
|
||
|
||
// 加载自定义转向半径
|
||
if (!reader.IsDBNull(reader.GetOrdinal("CustomTurnRadius")))
|
||
{
|
||
point.CustomTurnRadius = Convert.ToDouble(reader["CustomTurnRadius"]);
|
||
}
|
||
|
||
route.Points.Add(point);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存路径边
|
||
/// </summary>
|
||
private void SavePathEdge(string routeId, PathEdge edge, int sequenceNumber)
|
||
{
|
||
var sql = @"
|
||
INSERT INTO PathEdges
|
||
(Id, RouteId, StartPointId, EndPointId, SegmentType, PhysicalLength, SequenceNumber,
|
||
Ts_X, Ts_Y, Ts_Z, Te_X, Te_Y, Te_Z,
|
||
ArcCenter_X, ArcCenter_Y, ArcCenter_Z,
|
||
RequestedRadius, ActualRadius, DeflectionAngle, ArcLength)
|
||
VALUES (@id, @routeId, @startId, @endId, @segType, @length, @seqNum,
|
||
@tsx, @tsy, @tsz, @tex, @tey, @tez,
|
||
@acx, @acy, @acz,
|
||
@reqR, @actR, @angle, @arcLen)
|
||
";
|
||
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@id", edge.Id);
|
||
cmd.Parameters.AddWithValue("@routeId", routeId);
|
||
cmd.Parameters.AddWithValue("@startId", edge.StartPointId ?? "");
|
||
cmd.Parameters.AddWithValue("@endId", edge.EndPointId ?? "");
|
||
cmd.Parameters.AddWithValue("@segType", (int)edge.SegmentType);
|
||
cmd.Parameters.AddWithValue("@length", edge.PhysicalLength);
|
||
cmd.Parameters.AddWithValue("@seqNum", sequenceNumber);
|
||
|
||
// 直线段:保存起止点坐标
|
||
if (edge.SegmentType == PathSegmentType.Straight && edge.SampledPoints != null && edge.SampledPoints.Count >= 2)
|
||
{
|
||
cmd.Parameters.AddWithValue("@tsx", edge.SampledPoints.First().X);
|
||
cmd.Parameters.AddWithValue("@tsy", edge.SampledPoints.First().Y);
|
||
cmd.Parameters.AddWithValue("@tsz", edge.SampledPoints.First().Z);
|
||
cmd.Parameters.AddWithValue("@tex", edge.SampledPoints.Last().X);
|
||
cmd.Parameters.AddWithValue("@tey", edge.SampledPoints.Last().Y);
|
||
cmd.Parameters.AddWithValue("@tez", edge.SampledPoints.Last().Z);
|
||
|
||
// 圆弧参数为NULL
|
||
cmd.Parameters.AddWithValue("@acx", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@acy", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@acz", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@reqR", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@actR", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@angle", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@arcLen", DBNull.Value);
|
||
}
|
||
else if (edge.SegmentType == PathSegmentType.Arc && edge.Trajectory != null)
|
||
{
|
||
// 圆弧段:保存圆弧轨迹数据
|
||
cmd.Parameters.AddWithValue("@tsx", edge.Trajectory.Ts.X);
|
||
cmd.Parameters.AddWithValue("@tsy", edge.Trajectory.Ts.Y);
|
||
cmd.Parameters.AddWithValue("@tsz", edge.Trajectory.Ts.Z);
|
||
cmd.Parameters.AddWithValue("@tex", edge.Trajectory.Te.X);
|
||
cmd.Parameters.AddWithValue("@tey", edge.Trajectory.Te.Y);
|
||
cmd.Parameters.AddWithValue("@tez", edge.Trajectory.Te.Z);
|
||
cmd.Parameters.AddWithValue("@acx", edge.Trajectory.ArcCenter.X);
|
||
cmd.Parameters.AddWithValue("@acy", edge.Trajectory.ArcCenter.Y);
|
||
cmd.Parameters.AddWithValue("@acz", edge.Trajectory.ArcCenter.Z);
|
||
cmd.Parameters.AddWithValue("@reqR", edge.Trajectory.RequestedRadius);
|
||
cmd.Parameters.AddWithValue("@actR", edge.Trajectory.ActualRadius);
|
||
cmd.Parameters.AddWithValue("@angle", edge.Trajectory.DeflectionAngle);
|
||
cmd.Parameters.AddWithValue("@arcLen", edge.Trajectory.ArcLength);
|
||
}
|
||
else
|
||
{
|
||
// 其他情况,所有参数为NULL
|
||
cmd.Parameters.AddWithValue("@tsx", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@tsy", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@tsz", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@tex", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@tey", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@tez", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@acx", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@acy", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@acz", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@reqR", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@actR", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@angle", DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@arcLen", DBNull.Value);
|
||
}
|
||
|
||
cmd.ExecuteNonQuery();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除路径点
|
||
/// </summary>
|
||
private void DeletePathPoints(string routeId)
|
||
{
|
||
using (var cmd = new SQLiteCommand("DELETE FROM PathPoints WHERE RouteId = @routeId", _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@routeId", routeId);
|
||
cmd.ExecuteNonQuery();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除路径边
|
||
/// </summary>
|
||
private void DeletePathEdges(string routeId)
|
||
{
|
||
using (var cmd = new SQLiteCommand("DELETE FROM PathEdges WHERE RouteId = @routeId", _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@routeId", routeId);
|
||
cmd.ExecuteNonQuery();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载路径边
|
||
/// </summary>
|
||
private void LoadPathEdges(PathRoute route)
|
||
{
|
||
var sql = "SELECT * FROM PathEdges WHERE RouteId = @routeId ORDER BY SequenceNumber";
|
||
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
{
|
||
cmd.Parameters.AddWithValue("@routeId", route.Id);
|
||
|
||
using (var reader = cmd.ExecuteReader())
|
||
{
|
||
while (reader.Read())
|
||
{
|
||
var edge = new PathEdge
|
||
{
|
||
Id = reader["Id"].ToString(),
|
||
StartPointId = reader["StartPointId"].ToString(),
|
||
EndPointId = reader["EndPointId"].ToString(),
|
||
SegmentType = (PathSegmentType)Convert.ToInt32(reader["SegmentType"]),
|
||
PhysicalLength = Convert.ToDouble(reader["PhysicalLength"])
|
||
};
|
||
|
||
// 加载圆弧轨迹数据
|
||
if (edge.SegmentType == PathSegmentType.Arc)
|
||
{
|
||
edge.Trajectory = new ArcTrajectory
|
||
{
|
||
Ts = new Point3D(
|
||
Convert.ToDouble(reader["Ts_X"]),
|
||
Convert.ToDouble(reader["Ts_Y"]),
|
||
Convert.ToDouble(reader["Ts_Z"])),
|
||
Te = new Point3D(
|
||
Convert.ToDouble(reader["Te_X"]),
|
||
Convert.ToDouble(reader["Te_Y"]),
|
||
Convert.ToDouble(reader["Te_Z"])),
|
||
ArcCenter = new Point3D(
|
||
Convert.ToDouble(reader["ArcCenter_X"]),
|
||
Convert.ToDouble(reader["ArcCenter_Y"]),
|
||
Convert.ToDouble(reader["ArcCenter_Z"])),
|
||
RequestedRadius = Convert.ToDouble(reader["RequestedRadius"]),
|
||
ActualRadius = Convert.ToDouble(reader["ActualRadius"]),
|
||
DeflectionAngle = Convert.ToDouble(reader["DeflectionAngle"]),
|
||
ArcLength = Convert.ToDouble(reader["ArcLength"])
|
||
};
|
||
}
|
||
else if (edge.SegmentType == PathSegmentType.Straight)
|
||
{
|
||
// 直线段也加载Ts和Te(用于重新计算SampledPoints)
|
||
if (!reader.IsDBNull(reader.GetOrdinal("Ts_X")) &&
|
||
!reader.IsDBNull(reader.GetOrdinal("Te_X")))
|
||
{
|
||
edge.Trajectory = new ArcTrajectory
|
||
{
|
||
Ts = new Point3D(
|
||
Convert.ToDouble(reader["Ts_X"]),
|
||
Convert.ToDouble(reader["Ts_Y"]),
|
||
Convert.ToDouble(reader["Ts_Z"])),
|
||
Te = new Point3D(
|
||
Convert.ToDouble(reader["Te_X"]),
|
||
Convert.ToDouble(reader["Te_Y"]),
|
||
Convert.ToDouble(reader["Te_Z"])),
|
||
ArcCenter = new Point3D(0, 0, 0), // 直线段没有圆心
|
||
RequestedRadius = 0,
|
||
ActualRadius = 0,
|
||
DeflectionAngle = 0,
|
||
ArcLength = 0
|
||
};
|
||
}
|
||
}
|
||
|
||
route.Edges.Add(edge);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 重新计算SampledPoints(因为数据库中没有保存采样点)
|
||
if (route.Edges.Count > 0)
|
||
{
|
||
double samplingStep = ConfigManager.Instance.Current.PathEditing.ArcSamplingStep;
|
||
foreach (var edge in route.Edges)
|
||
{
|
||
if (edge.SegmentType == PathSegmentType.Arc && edge.Trajectory != null)
|
||
{
|
||
// 圆弧段:使用PathCurveEngine重新采样
|
||
edge.SampledPoints = PathCurveEngine.SampleArc(edge.Trajectory, samplingStep);
|
||
}
|
||
else if (edge.SegmentType == PathSegmentType.Straight)
|
||
{
|
||
// 直线段:从数据库加载的Ts和Te重新采样
|
||
Point3D startPoint = null;
|
||
Point3D endPoint = null;
|
||
|
||
// 尝试从Trajectory中获取(直线段也可能用Trajectory存储起止点)
|
||
if (edge.Trajectory != null)
|
||
{
|
||
startPoint = edge.Trajectory.Ts;
|
||
endPoint = edge.Trajectory.Te;
|
||
}
|
||
|
||
// 如果找到了起止点,重新采样
|
||
if (startPoint != null && endPoint != null)
|
||
{
|
||
int sampleCount = Math.Max(2, (int)Math.Ceiling(edge.PhysicalLength / samplingStep));
|
||
edge.SampledPoints = new List<Point3D>();
|
||
for (int i = 0; i <= sampleCount; i++)
|
||
{
|
||
double t = i / (double)sampleCount;
|
||
Point3D sampledPoint = startPoint + (endPoint - startPoint) * t;
|
||
edge.SampledPoints.Add(sampledPoint);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行非查询SQL语句
|
||
/// </summary>
|
||
private void ExecuteNonQuery(string sql, object parameters = null)
|
||
{
|
||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||
{
|
||
if (parameters != null)
|
||
{
|
||
var props = parameters.GetType().GetProperties();
|
||
foreach (var prop in props)
|
||
{
|
||
cmd.Parameters.AddWithValue($"@{prop.Name}", prop.GetValue(parameters));
|
||
}
|
||
}
|
||
cmd.ExecuteNonQuery();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取ModelItem的显示名称
|
||
/// </summary>
|
||
private string GetItemDisplayName(ModelItem item)
|
||
{
|
||
if (item == null)
|
||
return "未知对象";
|
||
|
||
try
|
||
{
|
||
return item.DisplayName ?? item.ToString();
|
||
}
|
||
catch
|
||
{
|
||
return "未知对象";
|
||
}
|
||
}
|
||
|
||
#region 批处理任务管理
|
||
|
||
/// <summary>
|
||
/// 创建批处理任务
|
||
/// </summary>
|
||
public async Task<int> CreateBatchTaskAsync(BatchCollisionTask task)
|
||
{
|
||
return await Task.Run(() =>
|
||
{
|
||
using (var cmd = new SQLiteCommand(_connection))
|
||
{
|
||
cmd.CommandText = @"
|
||
INSERT INTO BatchTasks (Name, Description, CreatedTime, Status, StartTime, EndTime, TotalItems, CompletedItems, FailedItems, LastModified)
|
||
VALUES (@Name, @Description, @CreatedTime, @Status, @StartTime, @EndTime, @TotalItems, @CompletedItems, @FailedItems, @LastModified);
|
||
SELECT last_insert_rowid();";
|
||
|
||
cmd.Parameters.AddWithValue("@Name", task.Name);
|
||
cmd.Parameters.AddWithValue("@Description", task.Description ?? "");
|
||
cmd.Parameters.AddWithValue("@CreatedTime", task.CreatedTime.ToString("o"));
|
||
cmd.Parameters.AddWithValue("@Status", task.Status.ToString());
|
||
cmd.Parameters.AddWithValue("@StartTime", task.StartTime?.ToString("o") ?? "");
|
||
cmd.Parameters.AddWithValue("@EndTime", task.EndTime?.ToString("o") ?? "");
|
||
cmd.Parameters.AddWithValue("@TotalItems", task.TotalItems);
|
||
cmd.Parameters.AddWithValue("@CompletedItems", task.CompletedItems);
|
||
cmd.Parameters.AddWithValue("@FailedItems", task.FailedItems);
|
||
cmd.Parameters.AddWithValue("@LastModified", task.LastModified.ToString("o"));
|
||
|
||
return Convert.ToInt32(cmd.ExecuteScalar());
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建批处理任务项
|
||
/// </summary>
|
||
public async Task<int> CreateBatchTaskItemAsync(BatchTaskItem item)
|
||
{
|
||
return await Task.Run(() =>
|
||
{
|
||
using (var cmd = new SQLiteCommand(_connection))
|
||
{
|
||
cmd.CommandText = @"
|
||
INSERT INTO BatchTaskItems (BatchTaskId, PathRouteId, PathRouteName, SequenceNumber, Status, StartTime, EndTime, ErrorMessage, ConfigOverride, VehicleObjectId, VehicleObjectName, DetectionItems, ClashDetectiveTestName)
|
||
VALUES (@BatchTaskId, @PathRouteId, @PathRouteName, @SequenceNumber, @Status, @StartTime, @EndTime, @ErrorMessage, @ConfigOverride, @VehicleObjectId, @VehicleObjectName, @DetectionItems, @ClashDetectiveTestName);
|
||
SELECT last_insert_rowid();";
|
||
|
||
cmd.Parameters.AddWithValue("@BatchTaskId", item.BatchTaskId);
|
||
cmd.Parameters.AddWithValue("@PathRouteId", item.PathRouteId);
|
||
cmd.Parameters.AddWithValue("@PathRouteName", item.PathRouteName ?? "");
|
||
cmd.Parameters.AddWithValue("@SequenceNumber", item.SequenceNumber);
|
||
cmd.Parameters.AddWithValue("@Status", item.Status.ToString());
|
||
cmd.Parameters.AddWithValue("@StartTime", item.StartTime?.ToString("o") ?? "");
|
||
cmd.Parameters.AddWithValue("@EndTime", item.EndTime?.ToString("o") ?? "");
|
||
cmd.Parameters.AddWithValue("@ErrorMessage", item.ErrorMessage ?? "");
|
||
cmd.Parameters.AddWithValue("@ConfigOverride", item.ConfigOverride?.ToJson() ?? "");
|
||
cmd.Parameters.AddWithValue("@VehicleObjectId", item.VehicleObjectId ?? "");
|
||
cmd.Parameters.AddWithValue("@VehicleObjectName", item.VehicleObjectName ?? "");
|
||
cmd.Parameters.AddWithValue("@DetectionItems", item.DetectionItems != null ? string.Join(",", item.DetectionItems) : "");
|
||
cmd.Parameters.AddWithValue("@ClashDetectiveTestName", item.ClashDetectiveTestName ?? "");
|
||
|
||
return Convert.ToInt32(cmd.ExecuteScalar());
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取批处理任务
|
||
/// </summary>
|
||
public async Task<BatchCollisionTask> GetBatchTaskAsync(int taskId)
|
||
{
|
||
return await Task.Run(() =>
|
||
{
|
||
using (var cmd = new SQLiteCommand(_connection))
|
||
{
|
||
cmd.CommandText = @"
|
||
SELECT Id, Name, Description, CreatedTime, Status, StartTime, EndTime, TotalItems, CompletedItems, FailedItems, LastModified
|
||
FROM BatchTasks
|
||
WHERE Id = @Id";
|
||
|
||
cmd.Parameters.AddWithValue("@Id", taskId);
|
||
|
||
using (var reader = cmd.ExecuteReader())
|
||
{
|
||
if (reader.Read())
|
||
{
|
||
return new BatchCollisionTask
|
||
{
|
||
Id = Convert.ToInt32(reader["Id"]),
|
||
Name = reader["Name"].ToString(),
|
||
Description = reader["Description"].ToString(),
|
||
CreatedTime = DateTime.Parse(reader["CreatedTime"].ToString()),
|
||
Status = (BatchTaskStatus)Enum.Parse(typeof(BatchTaskStatus), reader["Status"].ToString()),
|
||
StartTime = reader["StartTime"].ToString() != "" ? (DateTime?)DateTime.Parse(reader["StartTime"].ToString()) : null,
|
||
EndTime = reader["EndTime"].ToString() != "" ? (DateTime?)DateTime.Parse(reader["EndTime"].ToString()) : null,
|
||
TotalItems = Convert.ToInt32(reader["TotalItems"]),
|
||
CompletedItems = Convert.ToInt32(reader["CompletedItems"]),
|
||
FailedItems = Convert.ToInt32(reader["FailedItems"]),
|
||
LastModified = DateTime.Parse(reader["LastModified"].ToString())
|
||
};
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取所有批处理任务
|
||
/// </summary>
|
||
public async Task<List<BatchCollisionTask>> GetBatchTasksAsync(BatchTaskStatus? statusFilter = null, int limit = 50, int offset = 0)
|
||
{
|
||
return await Task.Run(() =>
|
||
{
|
||
var tasks = new List<BatchCollisionTask>();
|
||
using (var cmd = new SQLiteCommand(_connection))
|
||
{
|
||
var sql = @"
|
||
SELECT Id, Name, Description, CreatedTime, Status, StartTime, EndTime, TotalItems, CompletedItems, FailedItems, LastModified
|
||
FROM BatchTasks";
|
||
var conditions = new List<string>();
|
||
|
||
if (statusFilter.HasValue)
|
||
{
|
||
conditions.Add("Status = @Status");
|
||
cmd.Parameters.AddWithValue("@Status", statusFilter.Value.ToString());
|
||
}
|
||
|
||
if (conditions.Count > 0)
|
||
{
|
||
sql += " WHERE " + string.Join(" AND ", conditions);
|
||
}
|
||
|
||
sql += " ORDER BY CreatedTime DESC";
|
||
|
||
if (limit > 0)
|
||
{
|
||
sql += " LIMIT @Limit";
|
||
cmd.Parameters.AddWithValue("@Limit", limit);
|
||
}
|
||
|
||
if (offset > 0)
|
||
{
|
||
sql += " OFFSET @Offset";
|
||
cmd.Parameters.AddWithValue("@Offset", offset);
|
||
}
|
||
|
||
cmd.CommandText = sql;
|
||
|
||
using (var reader = cmd.ExecuteReader())
|
||
{
|
||
while (reader.Read())
|
||
{
|
||
tasks.Add(new BatchCollisionTask
|
||
{
|
||
Id = Convert.ToInt32(reader["Id"]),
|
||
Name = reader["Name"].ToString(),
|
||
Description = reader["Description"].ToString(),
|
||
CreatedTime = DateTime.Parse(reader["CreatedTime"].ToString()),
|
||
Status = (BatchTaskStatus)Enum.Parse(typeof(BatchTaskStatus), reader["Status"].ToString()),
|
||
StartTime = reader["StartTime"].ToString() != "" ? (DateTime?)DateTime.Parse(reader["StartTime"].ToString()) : null,
|
||
EndTime = reader["EndTime"].ToString() != "" ? (DateTime?)DateTime.Parse(reader["EndTime"].ToString()) : null,
|
||
TotalItems = Convert.ToInt32(reader["TotalItems"]),
|
||
CompletedItems = Convert.ToInt32(reader["CompletedItems"]),
|
||
FailedItems = Convert.ToInt32(reader["FailedItems"]),
|
||
LastModified = DateTime.Parse(reader["LastModified"].ToString())
|
||
});
|
||
}
|
||
}
|
||
}
|
||
return tasks;
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除批处理任务
|
||
/// </summary>
|
||
public async Task DeleteBatchTaskAsync(int taskId)
|
||
{
|
||
await Task.Run(() =>
|
||
{
|
||
using (var cmd = new SQLiteCommand(_connection))
|
||
{
|
||
cmd.CommandText = "DELETE FROM BatchTasks WHERE Id = @Id";
|
||
cmd.Parameters.AddWithValue("@Id", taskId);
|
||
cmd.ExecuteNonQuery();
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新批处理任务状态
|
||
/// </summary>
|
||
public async Task UpdateBatchTaskStatusAsync(int taskId, BatchTaskStatus status, DateTime? startTime = null, DateTime? endTime = null, int? completedItems = null, int? failedItems = null)
|
||
{
|
||
await Task.Run(() =>
|
||
{
|
||
using (var cmd = new SQLiteCommand(_connection))
|
||
{
|
||
var sql = "UPDATE BatchTasks SET Status = @Status, LastModified = @LastModified";
|
||
if (startTime.HasValue) sql += ", StartTime = @StartTime";
|
||
if (endTime.HasValue) sql += ", EndTime = @EndTime";
|
||
if (completedItems.HasValue) sql += ", CompletedItems = @CompletedItems";
|
||
if (failedItems.HasValue) sql += ", FailedItems = @FailedItems";
|
||
sql += " WHERE Id = @Id";
|
||
|
||
cmd.CommandText = sql;
|
||
cmd.Parameters.AddWithValue("@Status", status.ToString());
|
||
cmd.Parameters.AddWithValue("@LastModified", DateTime.Now.ToString("o"));
|
||
cmd.Parameters.AddWithValue("@Id", taskId);
|
||
|
||
if (startTime.HasValue) cmd.Parameters.AddWithValue("@StartTime", startTime.Value.ToString("o"));
|
||
if (endTime.HasValue) cmd.Parameters.AddWithValue("@EndTime", endTime.Value.ToString("o"));
|
||
if (completedItems.HasValue) cmd.Parameters.AddWithValue("@CompletedItems", completedItems.Value);
|
||
if (failedItems.HasValue) cmd.Parameters.AddWithValue("@FailedItems", failedItems.Value);
|
||
|
||
cmd.ExecuteNonQuery();
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新批处理任务项状态
|
||
/// </summary>
|
||
public async Task UpdateBatchTaskItemStatusAsync(int itemId, BatchTaskItemStatus status, DateTime? startTime = null, DateTime? endTime = null, string errorMessage = null, string clashDetectiveTestName = null)
|
||
{
|
||
await Task.Run(() =>
|
||
{
|
||
using (var cmd = new SQLiteCommand(_connection))
|
||
{
|
||
var sql = "UPDATE BatchTaskItems SET Status = @Status";
|
||
if (startTime.HasValue) sql += ", StartTime = @StartTime";
|
||
if (endTime.HasValue) sql += ", EndTime = @EndTime";
|
||
if (errorMessage != null) sql += ", ErrorMessage = @ErrorMessage";
|
||
if (clashDetectiveTestName != null) sql += ", ClashDetectiveTestName = @ClashDetectiveTestName";
|
||
sql += " WHERE Id = @Id";
|
||
|
||
cmd.CommandText = sql;
|
||
cmd.Parameters.AddWithValue("@Status", status.ToString());
|
||
cmd.Parameters.AddWithValue("@Id", itemId);
|
||
|
||
if (startTime.HasValue) cmd.Parameters.AddWithValue("@StartTime", startTime.Value.ToString("o"));
|
||
if (endTime.HasValue) cmd.Parameters.AddWithValue("@EndTime", endTime.Value.ToString("o"));
|
||
if (errorMessage != null) cmd.Parameters.AddWithValue("@ErrorMessage", errorMessage);
|
||
if (clashDetectiveTestName != null) cmd.Parameters.AddWithValue("@ClashDetectiveTestName", clashDetectiveTestName);
|
||
|
||
cmd.ExecuteNonQuery();
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取批处理任务的所有项
|
||
/// </summary>
|
||
public async Task<List<BatchTaskItem>> GetBatchTaskItemsAsync(int taskId)
|
||
{
|
||
return await Task.Run(() =>
|
||
{
|
||
var items = new List<BatchTaskItem>();
|
||
using (var cmd = new SQLiteCommand(_connection))
|
||
{
|
||
cmd.CommandText = @"
|
||
SELECT Id, BatchTaskId, PathRouteId, PathRouteName, SequenceNumber, Status, StartTime, EndTime, ErrorMessage, ConfigOverride, VehicleObjectId, VehicleObjectName, DetectionItems, ClashDetectiveTestName
|
||
FROM BatchTaskItems
|
||
WHERE BatchTaskId = @TaskId
|
||
ORDER BY SequenceNumber";
|
||
|
||
cmd.Parameters.AddWithValue("@TaskId", taskId);
|
||
|
||
using (var reader = cmd.ExecuteReader())
|
||
{
|
||
while (reader.Read())
|
||
{
|
||
items.Add(new BatchTaskItem
|
||
{
|
||
Id = Convert.ToInt32(reader["Id"]),
|
||
BatchTaskId = Convert.ToInt32(reader["BatchTaskId"]),
|
||
PathRouteId = reader["PathRouteId"].ToString(),
|
||
PathRouteName = reader["PathRouteName"].ToString(),
|
||
SequenceNumber = Convert.ToInt32(reader["SequenceNumber"]),
|
||
Status = (BatchTaskItemStatus)Enum.Parse(typeof(BatchTaskItemStatus), reader["Status"].ToString()),
|
||
StartTime = reader["StartTime"].ToString() != "" ? (DateTime?)DateTime.Parse(reader["StartTime"].ToString()) : null,
|
||
EndTime = reader["EndTime"].ToString() != "" ? (DateTime?)DateTime.Parse(reader["EndTime"].ToString()) : null,
|
||
ErrorMessage = reader["ErrorMessage"].ToString(),
|
||
ConfigOverride = CollisionDetectionConfig.FromJson(reader["ConfigOverride"].ToString()),
|
||
VehicleObjectId = reader["VehicleObjectId"].ToString(),
|
||
VehicleObjectName = reader["VehicleObjectName"].ToString(),
|
||
DetectionItems = reader["DetectionItems"].ToString() != "" ? reader["DetectionItems"].ToString().Split(',').ToList() : new List<string>(),
|
||
ClashDetectiveTestName = reader["ClashDetectiveTestName"].ToString()
|
||
});
|
||
}
|
||
}
|
||
}
|
||
return items;
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据测试名称获取碰撞检测结果
|
||
/// </summary>
|
||
public async Task<ClashDetectiveResultRecord> GetClashDetectiveResultByTestNameAsync(string testName)
|
||
{
|
||
return await Task.Run(() =>
|
||
{
|
||
using (var cmd = new SQLiteCommand(_connection))
|
||
{
|
||
cmd.CommandText = @"
|
||
SELECT Id, TestName, PathName, RouteId, TestTime, CollisionCount, AnimationCollisionCount, FrameRate, Duration, DetectionGap, AnimatedObjectName, IsVirtualVehicle, VehicleModelIndex, VehiclePathId, VirtualVehicleLength, VirtualVehicleWidth, VirtualVehicleHeight, CreatedAt
|
||
FROM ClashDetectiveResults
|
||
WHERE TestName = @TestName";
|
||
|
||
cmd.Parameters.AddWithValue("@TestName", testName);
|
||
|
||
using (var reader = cmd.ExecuteReader())
|
||
{
|
||
if (reader.Read())
|
||
{
|
||
return new ClashDetectiveResultRecord
|
||
{
|
||
Id = Convert.ToInt32(reader["Id"]),
|
||
TestName = reader["TestName"].ToString(),
|
||
PathName = reader["PathName"].ToString(),
|
||
RouteId = reader["RouteId"].ToString(),
|
||
TestTime = DateTime.Parse(reader["TestTime"].ToString()),
|
||
CollisionCount = Convert.ToInt32(reader["CollisionCount"]),
|
||
AnimationCollisionCount = Convert.ToInt32(reader["AnimationCollisionCount"]),
|
||
FrameRate = Convert.ToInt32(reader["FrameRate"]),
|
||
Duration = Convert.ToDouble(reader["Duration"]),
|
||
DetectionGap = Convert.ToDouble(reader["DetectionGap"]),
|
||
AnimatedObjectName = reader["AnimatedObjectName"].ToString(),
|
||
IsVirtualVehicle = Convert.ToBoolean(reader["IsVirtualVehicle"]),
|
||
VehicleModelIndex = !Convert.IsDBNull(reader["VehicleModelIndex"]) ? (int?)Convert.ToInt32(reader["VehicleModelIndex"]) : null,
|
||
VehiclePathId = reader["VehiclePathId"].ToString(),
|
||
VirtualVehicleLength = Convert.ToDouble(reader["VirtualVehicleLength"]),
|
||
VirtualVehicleWidth = Convert.ToDouble(reader["VirtualVehicleWidth"]),
|
||
VirtualVehicleHeight = Convert.ToDouble(reader["VirtualVehicleHeight"]),
|
||
CreatedAt = DateTime.Parse(reader["CreatedAt"].ToString())
|
||
};
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据路径ID获取路径
|
||
/// </summary>
|
||
public async Task<PathRoute> GetPathRouteAsync(string routeId)
|
||
{
|
||
return await Task.Run(() =>
|
||
{
|
||
using (var cmd = new SQLiteCommand(_connection))
|
||
{
|
||
cmd.CommandText = @"
|
||
SELECT Id, Name, CreatedTime, LastModified, TotalLength
|
||
FROM PathRoutes
|
||
WHERE Id = @Id";
|
||
|
||
cmd.Parameters.AddWithValue("@Id", routeId);
|
||
|
||
using (var reader = cmd.ExecuteReader())
|
||
{
|
||
if (reader.Read())
|
||
{
|
||
var route = new PathRoute(reader["Name"].ToString())
|
||
{
|
||
Id = reader["Id"].ToString(),
|
||
CreatedTime = DateTime.Parse(reader["CreatedTime"].ToString()),
|
||
LastModified = DateTime.Parse(reader["LastModified"].ToString()),
|
||
TotalLength = Convert.ToDouble(reader["TotalLength"])
|
||
};
|
||
|
||
// 加载路径点和路径边
|
||
LoadPathPoints(route);
|
||
LoadPathEdges(route);
|
||
return route;
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 批处理队列管理(简化版)
|
||
|
||
/// <summary>
|
||
/// 创建批处理队列项
|
||
/// </summary>
|
||
public async Task<int> CreateBatchQueueItemAsync(BatchQueueItem item)
|
||
{
|
||
return await Task.Run(() =>
|
||
{
|
||
using (var cmd = new SQLiteCommand(_connection))
|
||
{
|
||
cmd.CommandText = @"
|
||
INSERT INTO BatchQueueItems (
|
||
PathRouteId, PathRouteName, Status, CreatedTime, StartTime, EndTime, ErrorMessage,
|
||
FrameRate, DurationSeconds, DetectionToleranceMeters,
|
||
IsVirtualVehicle, VirtualVehicleLength, VirtualVehicleWidth, VirtualVehicleHeight,
|
||
VehicleObjectId, VehicleObjectName, DetectionItems, DetectAllObjects,
|
||
ClashDetectiveTestName, CollisionCount
|
||
)
|
||
VALUES (
|
||
@PathRouteId, @PathRouteName, @Status, @CreatedTime, @StartTime, @EndTime, @ErrorMessage,
|
||
@FrameRate, @DurationSeconds, @DetectionToleranceMeters,
|
||
@IsVirtualVehicle, @VirtualVehicleLength, @VirtualVehicleWidth, @VirtualVehicleHeight,
|
||
@VehicleObjectId, @VehicleObjectName, @DetectionItems, @DetectAllObjects,
|
||
@ClashDetectiveTestName, @CollisionCount
|
||
);
|
||
SELECT last_insert_rowid();";
|
||
|
||
cmd.Parameters.AddWithValue("@PathRouteId", item.PathRouteId);
|
||
cmd.Parameters.AddWithValue("@PathRouteName", item.PathRouteName ?? "");
|
||
cmd.Parameters.AddWithValue("@Status", item.Status.ToString());
|
||
cmd.Parameters.AddWithValue("@CreatedTime", item.CreatedTime.ToString("o"));
|
||
cmd.Parameters.AddWithValue("@StartTime", item.StartTime?.ToString("o") ?? "");
|
||
cmd.Parameters.AddWithValue("@EndTime", item.EndTime?.ToString("o") ?? "");
|
||
cmd.Parameters.AddWithValue("@ErrorMessage", item.ErrorMessage ?? "");
|
||
cmd.Parameters.AddWithValue("@FrameRate", item.FrameRate);
|
||
cmd.Parameters.AddWithValue("@DurationSeconds", item.DurationSeconds);
|
||
cmd.Parameters.AddWithValue("@DetectionToleranceMeters", item.DetectionToleranceMeters);
|
||
cmd.Parameters.AddWithValue("@IsVirtualVehicle", item.IsVirtualVehicle ? 1 : 0);
|
||
cmd.Parameters.AddWithValue("@VirtualVehicleLength", item.VirtualVehicleLength);
|
||
cmd.Parameters.AddWithValue("@VirtualVehicleWidth", item.VirtualVehicleWidth);
|
||
cmd.Parameters.AddWithValue("@VirtualVehicleHeight", item.VirtualVehicleHeight);
|
||
cmd.Parameters.AddWithValue("@VehicleObjectId", item.VehicleObjectId ?? "");
|
||
cmd.Parameters.AddWithValue("@VehicleObjectName", item.VehicleObjectName ?? "");
|
||
cmd.Parameters.AddWithValue("@DetectionItems", item.DetectionItems != null ? string.Join(",", item.DetectionItems) : "");
|
||
cmd.Parameters.AddWithValue("@DetectAllObjects", item.DetectAllObjects ? 1 : 0);
|
||
cmd.Parameters.AddWithValue("@ClashDetectiveTestName", item.ClashDetectiveTestName ?? "");
|
||
cmd.Parameters.AddWithValue("@CollisionCount", item.CollisionCount ?? (object)DBNull.Value);
|
||
|
||
return Convert.ToInt32(cmd.ExecuteScalar());
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取批处理队列项
|
||
/// </summary>
|
||
public async Task<BatchQueueItem> GetBatchQueueItemAsync(int itemId)
|
||
{
|
||
return await Task.Run(() =>
|
||
{
|
||
using (var cmd = new SQLiteCommand(_connection))
|
||
{
|
||
cmd.CommandText = @"
|
||
SELECT Id, PathRouteId, PathRouteName, Status, CreatedTime, StartTime, EndTime, ErrorMessage,
|
||
FrameRate, DurationSeconds, DetectionToleranceMeters,
|
||
IsVirtualVehicle, VirtualVehicleLength, VirtualVehicleWidth, VirtualVehicleHeight,
|
||
VehicleObjectId, VehicleObjectName, DetectionItems, DetectAllObjects,
|
||
ClashDetectiveTestName, CollisionCount
|
||
FROM BatchQueueItems
|
||
WHERE Id = @Id";
|
||
|
||
cmd.Parameters.AddWithValue("@Id", itemId);
|
||
|
||
using (var reader = cmd.ExecuteReader())
|
||
{
|
||
if (reader.Read())
|
||
{
|
||
return new BatchQueueItem
|
||
{
|
||
Id = Convert.ToInt32(reader["Id"]),
|
||
PathRouteId = reader["PathRouteId"].ToString(),
|
||
PathRouteName = reader["PathRouteName"].ToString(),
|
||
Status = (BatchQueueStatus)Enum.Parse(typeof(BatchQueueStatus), reader["Status"].ToString()),
|
||
CreatedTime = DateTime.Parse(reader["CreatedTime"].ToString()),
|
||
StartTime = reader["StartTime"].ToString() != "" ? (DateTime?)DateTime.Parse(reader["StartTime"].ToString()) : null,
|
||
EndTime = reader["EndTime"].ToString() != "" ? (DateTime?)DateTime.Parse(reader["EndTime"].ToString()) : null,
|
||
ErrorMessage = reader["ErrorMessage"].ToString(),
|
||
FrameRate = Convert.ToInt32(reader["FrameRate"]),
|
||
DurationSeconds = Convert.ToDouble(reader["DurationSeconds"]),
|
||
DetectionToleranceMeters = Convert.ToDouble(reader["DetectionToleranceMeters"]),
|
||
IsVirtualVehicle = Convert.ToBoolean(reader["IsVirtualVehicle"]),
|
||
VirtualVehicleLength = Convert.ToDouble(reader["VirtualVehicleLength"]),
|
||
VirtualVehicleWidth = Convert.ToDouble(reader["VirtualVehicleWidth"]),
|
||
VirtualVehicleHeight = Convert.ToDouble(reader["VirtualVehicleHeight"]),
|
||
VehicleObjectId = reader["VehicleObjectId"].ToString(),
|
||
VehicleObjectName = reader["VehicleObjectName"].ToString(),
|
||
DetectionItems = reader["DetectionItems"].ToString() != "" ? reader["DetectionItems"].ToString().Split(',').ToList() : new List<string>(),
|
||
DetectAllObjects = Convert.ToBoolean(reader["DetectAllObjects"]),
|
||
ClashDetectiveTestName = reader["ClashDetectiveTestName"].ToString(),
|
||
CollisionCount = !Convert.IsDBNull(reader["CollisionCount"]) ? (int?)Convert.ToInt32(reader["CollisionCount"]) : null
|
||
};
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取批处理队列项列表
|
||
/// </summary>
|
||
public async Task<List<BatchQueueItem>> GetBatchQueueItemsAsync(BatchQueueStatus statusFilter = BatchQueueStatus.All, int limit = 50, int offset = 0)
|
||
{
|
||
return await Task.Run(() =>
|
||
{
|
||
var items = new List<BatchQueueItem>();
|
||
using (var cmd = new SQLiteCommand(_connection))
|
||
{
|
||
var sql = @"
|
||
SELECT Id, PathRouteId, PathRouteName, Status, CreatedTime, StartTime, EndTime, ErrorMessage,
|
||
FrameRate, DurationSeconds, DetectionToleranceMeters,
|
||
IsVirtualVehicle, VirtualVehicleLength, VirtualVehicleWidth, VirtualVehicleHeight,
|
||
VehicleObjectId, VehicleObjectName, DetectionItems, DetectAllObjects,
|
||
ClashDetectiveTestName, CollisionCount
|
||
FROM BatchQueueItems";
|
||
var conditions = new List<string>();
|
||
|
||
// 只有当 statusFilter 不是 All 时才添加筛选条件
|
||
if (statusFilter != BatchQueueStatus.All)
|
||
{
|
||
conditions.Add("Status = @Status");
|
||
cmd.Parameters.AddWithValue("@Status", statusFilter.ToString());
|
||
}
|
||
|
||
if (conditions.Count > 0)
|
||
{
|
||
sql += " WHERE " + string.Join(" AND ", conditions);
|
||
}
|
||
|
||
sql += " ORDER BY CreatedTime DESC";
|
||
|
||
if (limit > 0)
|
||
{
|
||
sql += " LIMIT @Limit";
|
||
cmd.Parameters.AddWithValue("@Limit", limit);
|
||
}
|
||
|
||
if (offset > 0)
|
||
{
|
||
sql += " OFFSET @Offset";
|
||
cmd.Parameters.AddWithValue("@Offset", offset);
|
||
}
|
||
|
||
cmd.CommandText = sql;
|
||
|
||
using (var reader = cmd.ExecuteReader())
|
||
{
|
||
while (reader.Read())
|
||
{
|
||
items.Add(new BatchQueueItem
|
||
{
|
||
Id = Convert.ToInt32(reader["Id"]),
|
||
PathRouteId = reader["PathRouteId"].ToString(),
|
||
PathRouteName = reader["PathRouteName"].ToString(),
|
||
Status = (BatchQueueStatus)Enum.Parse(typeof(BatchQueueStatus), reader["Status"].ToString()),
|
||
CreatedTime = DateTime.Parse(reader["CreatedTime"].ToString()),
|
||
StartTime = reader["StartTime"].ToString() != "" ? (DateTime?)DateTime.Parse(reader["StartTime"].ToString()) : null,
|
||
EndTime = reader["EndTime"].ToString() != "" ? (DateTime?)DateTime.Parse(reader["EndTime"].ToString()) : null,
|
||
ErrorMessage = reader["ErrorMessage"].ToString(),
|
||
FrameRate = Convert.ToInt32(reader["FrameRate"]),
|
||
DurationSeconds = Convert.ToDouble(reader["DurationSeconds"]),
|
||
DetectionToleranceMeters = Convert.ToDouble(reader["DetectionToleranceMeters"]),
|
||
IsVirtualVehicle = Convert.ToBoolean(reader["IsVirtualVehicle"]),
|
||
VirtualVehicleLength = Convert.ToDouble(reader["VirtualVehicleLength"]),
|
||
VirtualVehicleWidth = Convert.ToDouble(reader["VirtualVehicleWidth"]),
|
||
VirtualVehicleHeight = Convert.ToDouble(reader["VirtualVehicleHeight"]),
|
||
VehicleObjectId = reader["VehicleObjectId"].ToString(),
|
||
VehicleObjectName = reader["VehicleObjectName"].ToString(),
|
||
DetectionItems = reader["DetectionItems"].ToString() != "" ? reader["DetectionItems"].ToString().Split(',').ToList() : new List<string>(),
|
||
DetectAllObjects = Convert.ToBoolean(reader["DetectAllObjects"]),
|
||
ClashDetectiveTestName = reader["ClashDetectiveTestName"].ToString(),
|
||
CollisionCount = !Convert.IsDBNull(reader["CollisionCount"]) ? (int?)Convert.ToInt32(reader["CollisionCount"]) : null
|
||
});
|
||
}
|
||
}
|
||
}
|
||
return items;
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新批处理队列项
|
||
/// </summary>
|
||
public async Task UpdateBatchQueueItemAsync(BatchQueueItem item)
|
||
{
|
||
await Task.Run(() =>
|
||
{
|
||
using (var cmd = new SQLiteCommand(_connection))
|
||
{
|
||
cmd.CommandText = @"
|
||
UPDATE BatchQueueItems
|
||
SET Status = @Status, StartTime = @StartTime, EndTime = @EndTime, ErrorMessage = @ErrorMessage,
|
||
ClashDetectiveTestName = @ClashDetectiveTestName, CollisionCount = @CollisionCount
|
||
WHERE Id = @Id";
|
||
|
||
cmd.Parameters.AddWithValue("@Status", item.Status.ToString());
|
||
cmd.Parameters.AddWithValue("@StartTime", item.StartTime?.ToString("o") ?? "");
|
||
cmd.Parameters.AddWithValue("@EndTime", item.EndTime?.ToString("o") ?? "");
|
||
cmd.Parameters.AddWithValue("@ErrorMessage", item.ErrorMessage ?? "");
|
||
cmd.Parameters.AddWithValue("@ClashDetectiveTestName", item.ClashDetectiveTestName ?? "");
|
||
cmd.Parameters.AddWithValue("@CollisionCount", item.CollisionCount ?? (object)DBNull.Value);
|
||
cmd.Parameters.AddWithValue("@Id", item.Id);
|
||
|
||
cmd.ExecuteNonQuery();
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除批处理队列项
|
||
/// </summary>
|
||
public async Task DeleteBatchQueueItemAsync(int itemId)
|
||
{
|
||
await Task.Run(() =>
|
||
{
|
||
using (var cmd = new SQLiteCommand(_connection))
|
||
{
|
||
cmd.CommandText = "DELETE FROM BatchQueueItems WHERE Id = @Id";
|
||
cmd.Parameters.AddWithValue("@Id", itemId);
|
||
cmd.ExecuteNonQuery();
|
||
}
|
||
});
|
||
}
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 释放资源
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
Dispose(true);
|
||
GC.SuppressFinalize(this);
|
||
}
|
||
|
||
protected virtual void Dispose(bool disposing)
|
||
{
|
||
if (disposing)
|
||
{
|
||
_connection?.Close();
|
||
_connection?.Dispose();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// ClashDetective碰撞检测结果记录
|
||
/// </summary>
|
||
public class ClashDetectiveResultRecord
|
||
{
|
||
public int Id { get; set; }
|
||
public string TestName { get; set; }
|
||
public string PathName { get; set; }
|
||
public string RouteId { get; set; }
|
||
public DateTime TestTime { get; set; }
|
||
public int CollisionCount { get; set; }
|
||
public int AnimationCollisionCount { get; set; }
|
||
public int FrameRate { get; set; }
|
||
public double Duration { get; set; }
|
||
public double DetectionGap { get; set; }
|
||
public string AnimatedObjectName { get; set; }
|
||
public bool IsVirtualVehicle { get; set; }
|
||
public int? VehicleModelIndex { get; set; }
|
||
public string VehiclePathId { get; set; }
|
||
public double VirtualVehicleLength { get; set; }
|
||
public double VirtualVehicleWidth { get; set; }
|
||
public double VirtualVehicleHeight { get; set; }
|
||
public DateTime CreatedAt { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// ClashDetective碰撞对象数据模型
|
||
/// </summary>
|
||
public class ClashDetectiveCollisionObjectRecord
|
||
{
|
||
public int Id { get; set; }
|
||
public int ResultId { get; set; }
|
||
public int? ModelIndex { get; set; }
|
||
public string PathId { get; set; }
|
||
public string DisplayName { get; set; }
|
||
public string ObjectName { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 路径分析结果数据模型
|
||
/// </summary>
|
||
public class PathAnalysisResult
|
||
{
|
||
public string RouteId { get; set; }
|
||
public int CollisionCount { get; set; }
|
||
public double SafetyScore { get; set; }
|
||
public double EfficiencyScore { get; set; }
|
||
public double OverallScore { get; set; }
|
||
public DateTime AnalysisTime { get; set; }
|
||
public string Strategy { get; set; }
|
||
}
|
||
}
|