实现了基本的路径分析功能,增加了文档关联的sqllite数据库
This commit is contained in:
parent
4357b91446
commit
6091b794de
@ -155,7 +155,9 @@
|
||||
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" AStarTestRunner.csproj)",
|
||||
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" NavisworksTransport.UnitTests.csproj)",
|
||||
"Bash(\"bin\\Debug\\AStarTestRunner.exe\")",
|
||||
"Bash(git restore:*)"
|
||||
"Bash(git restore:*)",
|
||||
"Read(//c/Program Files/Autodesk/Navisworks Manage 2026/**)",
|
||||
"Bash(where:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"additionalDirectories": [
|
||||
|
||||
@ -91,7 +91,13 @@
|
||||
<HintPath>packages\RoyT.AStar.3.0.2\lib\netstandard2.0\Roy-T.AStar.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
|
||||
|
||||
<!-- System.Data.SQLite NuGet Package -->
|
||||
<Reference Include="System.Data.SQLite">
|
||||
<HintPath>packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.118.0\lib\net46\System.Data.SQLite.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -106,6 +112,8 @@
|
||||
<Compile Include="src\Core\NavigationMapGenerator.cs" />
|
||||
<Compile Include="src\Core\PathDataManager.cs" />
|
||||
<Compile Include="src\Core\PathPlanningManager.cs" />
|
||||
<Compile Include="src\Core\PathDatabase.cs" />
|
||||
<Compile Include="src\Core\PathAnalysisService.cs" />
|
||||
<Compile Include="src\Core\PathPlanningModels.cs" />
|
||||
|
||||
<!-- Core - Events and Interfaces -->
|
||||
|
||||
35
clean_database.bat
Normal file
35
clean_database.bat
Normal file
@ -0,0 +1,35 @@
|
||||
@echo off
|
||||
echo ========================================
|
||||
echo NavisworksTransport 数据库清理工具
|
||||
echo ========================================
|
||||
echo.
|
||||
echo 此脚本将删除所有现有的路径数据库文件(.db文件)
|
||||
echo 下次启动插件时将自动创建新的数据库结构
|
||||
echo.
|
||||
echo 警告:此操作将删除所有历史路径数据!
|
||||
echo.
|
||||
pause
|
||||
|
||||
REM 设置数据库文件所在目录
|
||||
set DB_DIR=C:\Users\Tellme\Documents\NavisworksTransport\分层输出
|
||||
|
||||
echo.
|
||||
echo 正在清理数据库文件...
|
||||
echo 目录: %DB_DIR%
|
||||
echo.
|
||||
|
||||
REM 删除所有.db文件
|
||||
if exist "%DB_DIR%\*.db" (
|
||||
del /f /q "%DB_DIR%\*.db"
|
||||
echo 已删除所有数据库文件
|
||||
) else (
|
||||
echo 未找到数据库文件
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ========================================
|
||||
echo 数据库清理完成!
|
||||
echo 下次启动Navisworks插件时将自动创建新的数据库
|
||||
echo ========================================
|
||||
echo.
|
||||
pause
|
||||
@ -2,6 +2,10 @@
|
||||
|
||||
## 功能点
|
||||
|
||||
### [2025/10/01]
|
||||
|
||||
1. [x] (功能)路径分析
|
||||
|
||||
### [2025/09/29]
|
||||
|
||||
1. [x] (功能)导出导航地图为图片
|
||||
|
||||
@ -4,4 +4,5 @@
|
||||
<package id="NETStandard.Library" version="2.0.3" targetFramework="net48" />
|
||||
<package id="MSTest.TestFramework" version="3.0.4" targetFramework="net48" />
|
||||
<package id="MSTest.TestAdapter" version="3.0.4" targetFramework="net48" />
|
||||
<package id="System.Data.SQLite.Core" version="1.0.118.0" targetFramework="net48" />
|
||||
</packages>
|
||||
@ -471,10 +471,24 @@ namespace NavisworksTransport
|
||||
LogManager.Info($"[文档管理] 文档文件名: {activeDoc?.FileName ?? "空"}");
|
||||
LogManager.Info($"[文档管理] 文档标题: {activeDoc?.Title ?? "空"}");
|
||||
|
||||
// 分析状态变化类型
|
||||
// 分析状态变化类型并执行相应操作
|
||||
if (!_hasModels && currentHasModels)
|
||||
{
|
||||
LogManager.Info($"[文档管理] *** 状态分析: 从无到有(可能是打开文档)***");
|
||||
|
||||
// 文档加载完成,初始化PathDatabase
|
||||
if (!string.IsNullOrEmpty(activeDoc?.FileName))
|
||||
{
|
||||
GlobalExceptionHandler.SafeExecute(() =>
|
||||
{
|
||||
var pathManager = PathPlanningManager.GetActivePathManager();
|
||||
if (pathManager != null)
|
||||
{
|
||||
pathManager.EnsureDatabaseInitialized();
|
||||
LogManager.Info("[文档管理] 已触发PathDatabase初始化");
|
||||
}
|
||||
}, "初始化PathDatabase");
|
||||
}
|
||||
}
|
||||
else if (_hasModels && !currentHasModels)
|
||||
{
|
||||
@ -488,14 +502,11 @@ namespace NavisworksTransport
|
||||
{
|
||||
LogManager.Info($"[文档管理] *** 状态分析: 保持有模型状态(模型数量变化)***");
|
||||
}
|
||||
|
||||
|
||||
LogManager.Info("========================================");
|
||||
|
||||
|
||||
// 更新状态
|
||||
_hasModels = currentHasModels;
|
||||
|
||||
// 暂时不执行任何清理或初始化操作
|
||||
LogManager.Info("[文档管理] 观察模式:不执行清理或初始化操作");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
268
src/Core/PathAnalysisService.cs
Normal file
268
src/Core/PathAnalysisService.cs
Normal file
@ -0,0 +1,268 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
namespace NavisworksTransport
|
||||
{
|
||||
/// <summary>
|
||||
/// 路径分析服务 - 简化版
|
||||
/// 负责路径碰撞分析和评分计算
|
||||
/// </summary>
|
||||
public class PathAnalysisService
|
||||
{
|
||||
private readonly PathDatabase _database;
|
||||
|
||||
public PathAnalysisService(PathDatabase database)
|
||||
{
|
||||
_database = database ?? throw new ArgumentNullException(nameof(database));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分析单条路径
|
||||
/// </summary>
|
||||
/// <param name="route">路径对象</param>
|
||||
/// <param name="strategy">分析策略:安全优先/效率优先</param>
|
||||
/// <returns>分析结果</returns>
|
||||
public PathAnalysisResult AnalyzePath(PathRoute route, string strategy = "安全优先")
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. 执行碰撞检测
|
||||
var collisions = DetectCollisions(route);
|
||||
|
||||
// 2. 保存碰撞结果到数据库
|
||||
_database.SaveCollisions(route.Id, collisions);
|
||||
|
||||
// 3. 计算评分
|
||||
var safetyScore = CalculateSafetyScore(collisions.Count, route.TotalLength);
|
||||
var efficiencyScore = CalculateEfficiencyScore(route.TotalLength, route.EstimatedTime);
|
||||
|
||||
// 4. 保存分析结果到数据库
|
||||
_database.SaveAnalysisScore(route.Id, collisions.Count, safetyScore, efficiencyScore, strategy);
|
||||
|
||||
LogManager.Info($"路径分析完成: {route.Name}, 碰撞={collisions.Count}, 安全分={safetyScore:F1}, 效率分={efficiencyScore:F1}");
|
||||
|
||||
return _database.GetAnalysisResult(route.Id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"路径分析失败: {route?.Name ?? "未知路径"}, {ex.Message}", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对比多条路径,返回最佳路径和建议
|
||||
/// </summary>
|
||||
/// <param name="routes">路径列表</param>
|
||||
/// <param name="strategy">分析策略</param>
|
||||
/// <returns>对比结果</returns>
|
||||
public PathComparisonResult CompareRoutes(List<PathRoute> routes, string strategy = "安全优先")
|
||||
{
|
||||
if (routes == null || routes.Count == 0)
|
||||
throw new ArgumentException("路径列表不能为空");
|
||||
|
||||
var result = new PathComparisonResult
|
||||
{
|
||||
Strategy = strategy,
|
||||
Analyses = new List<PathAnalysisResult>()
|
||||
};
|
||||
|
||||
// 1. 分析每条路径
|
||||
foreach (var route in routes)
|
||||
{
|
||||
var analysis = AnalyzePath(route, strategy);
|
||||
result.Analyses.Add(analysis);
|
||||
}
|
||||
|
||||
// 2. 根据策略选择最佳路径
|
||||
PathAnalysisResult bestRoute;
|
||||
if (strategy == "安全优先")
|
||||
{
|
||||
// 优先选择碰撞少、安全分高的路径
|
||||
bestRoute = result.Analyses
|
||||
.OrderBy(a => a.CollisionCount)
|
||||
.ThenByDescending(a => a.SafetyScore)
|
||||
.First();
|
||||
}
|
||||
else // 效率优先
|
||||
{
|
||||
// 优先选择效率分高、长度短的路径
|
||||
bestRoute = result.Analyses
|
||||
.OrderByDescending(a => a.EfficiencyScore)
|
||||
.ThenBy(a => a.CollisionCount)
|
||||
.First();
|
||||
}
|
||||
|
||||
result.BestRouteId = bestRoute.RouteId;
|
||||
result.BestRouteName = routes.First(r => r.Id == bestRoute.RouteId).Name;
|
||||
|
||||
// 3. 生成优化建议
|
||||
result.Suggestions = GenerateSuggestions(result.Analyses, routes, strategy);
|
||||
|
||||
LogManager.Info($"路径对比完成: 共{routes.Count}条路径, 最佳路径={result.BestRouteName}, 策略={strategy}");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行碰撞检测
|
||||
/// 注意:实际的碰撞检测需要在路径动画执行时进行
|
||||
/// 这里优先从数据库加载已有的碰撞结果
|
||||
/// </summary>
|
||||
private List<CollisionResult> DetectCollisions(PathRoute route)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 先尝试从数据库获取已有的碰撞数据
|
||||
var existingCount = _database.GetCollisionCount(route.Id);
|
||||
|
||||
if (existingCount > 0)
|
||||
{
|
||||
LogManager.Info($"从数据库加载碰撞数据: {route.Name}, 碰撞数={existingCount}");
|
||||
// 返回空列表,碰撞数量已经从数据库获取
|
||||
return new List<CollisionResult>();
|
||||
}
|
||||
|
||||
// 如果数据库没有碰撞数据,返回空列表
|
||||
// TODO: 未来可以在这里触发实际的碰撞检测
|
||||
LogManager.Info($"路径{route.Name}暂无碰撞数据");
|
||||
return new List<CollisionResult>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取碰撞数据失败: {route.Name}, {ex.Message}", ex);
|
||||
return new List<CollisionResult>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算安全评分(100分制)
|
||||
/// 碰撞越少分数越高
|
||||
/// </summary>
|
||||
private double CalculateSafetyScore(int collisionCount, double pathLength)
|
||||
{
|
||||
// 基础分:100分
|
||||
double baseScore = 100.0;
|
||||
|
||||
// 每次碰撞扣10分
|
||||
double collisionPenalty = collisionCount * 10.0;
|
||||
|
||||
// 长路径额外扣分(每100米扣1分)
|
||||
double lengthPenalty = (pathLength / 100.0) * 0.5;
|
||||
|
||||
double score = baseScore - collisionPenalty - lengthPenalty;
|
||||
|
||||
// 分数不低于0
|
||||
return Math.Max(0, score);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算效率评分(100分制)
|
||||
/// 路径越短、速度越快分数越高
|
||||
/// </summary>
|
||||
private double CalculateEfficiencyScore(double pathLength, double estimatedTime)
|
||||
{
|
||||
if (estimatedTime <= 0)
|
||||
return 0;
|
||||
|
||||
// 计算平均速度 (m/s)
|
||||
double avgSpeed = pathLength / estimatedTime;
|
||||
|
||||
// 速度分数:速度越快分数越高
|
||||
// 假设理想速度为1.0 m/s,速度每提升0.1则加10分
|
||||
double speedScore = Math.Min(100, avgSpeed * 100);
|
||||
|
||||
// 路径长度惩罚:路径越短越好
|
||||
// 基准长度100米,每超过10米扣1分
|
||||
double lengthPenalty = Math.Max(0, (pathLength - 100) / 10);
|
||||
|
||||
double score = speedScore - lengthPenalty;
|
||||
|
||||
return Math.Max(0, Math.Min(100, score));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成优化建议(基于简单规则)
|
||||
/// </summary>
|
||||
private List<string> GenerateSuggestions(List<PathAnalysisResult> analyses,
|
||||
List<PathRoute> routes, string strategy)
|
||||
{
|
||||
var suggestions = new List<string>();
|
||||
|
||||
// 规则1:碰撞过多
|
||||
var maxCollision = analyses.Max(a => a.CollisionCount);
|
||||
if (maxCollision > 5)
|
||||
{
|
||||
suggestions.Add($"✅ 优先处理碰撞热点,当前最多{maxCollision}次碰撞");
|
||||
}
|
||||
else if (maxCollision > 0)
|
||||
{
|
||||
suggestions.Add($"⚠️ 存在{maxCollision}次碰撞,建议优化路径避让");
|
||||
}
|
||||
|
||||
// 规则2:路径长度差异大
|
||||
var avgLength = routes.Average(r => r.TotalLength);
|
||||
var longRoutes = routes.Where(r => r.TotalLength > avgLength * 1.3).ToList();
|
||||
if (longRoutes.Count > 0)
|
||||
{
|
||||
suggestions.Add($"✅ {longRoutes.Count}条路径偏长(超过平均值30%),建议优化路线");
|
||||
}
|
||||
|
||||
// 规则3:时间效率建议
|
||||
var avgTime = routes.Average(r => r.EstimatedTime);
|
||||
var slowRoutes = routes.Where(r => r.EstimatedTime > avgTime * 1.2).ToList();
|
||||
if (slowRoutes.Count > 0)
|
||||
{
|
||||
suggestions.Add($"⚠️ {slowRoutes.Count}条路径耗时较长,考虑提高通行速度或缩短路径");
|
||||
}
|
||||
|
||||
// 规则4:零碰撞路径推荐
|
||||
var zeroCollisionRoutes = analyses.Where(a => a.CollisionCount == 0).ToList();
|
||||
if (zeroCollisionRoutes.Count > 0)
|
||||
{
|
||||
suggestions.Add($"✅ {zeroCollisionRoutes.Count}条路径无碰撞,优先推荐使用");
|
||||
}
|
||||
|
||||
// 规则5:策略特定建议
|
||||
if (strategy == "安全优先")
|
||||
{
|
||||
suggestions.Add("💡 当前策略:安全优先 - 建议选择碰撞最少的路径");
|
||||
}
|
||||
else
|
||||
{
|
||||
suggestions.Add("💡 当前策略:效率优先 - 建议选择最短路径,但需注意安全");
|
||||
}
|
||||
|
||||
// 如果没有任何建议,添加默认建议
|
||||
if (suggestions.Count == 0)
|
||||
{
|
||||
suggestions.Add("✅ 所有路径表现良好,可根据实际需求选择");
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 路径对比结果
|
||||
/// </summary>
|
||||
public class PathComparisonResult
|
||||
{
|
||||
/// <summary>最佳路径ID</summary>
|
||||
public string BestRouteId { get; set; }
|
||||
|
||||
/// <summary>最佳路径名称</summary>
|
||||
public string BestRouteName { get; set; }
|
||||
|
||||
/// <summary>分析策略</summary>
|
||||
public string Strategy { get; set; }
|
||||
|
||||
/// <summary>所有路径的分析结果</summary>
|
||||
public List<PathAnalysisResult> Analyses { get; set; }
|
||||
|
||||
/// <summary>优化建议列表</summary>
|
||||
public List<string> Suggestions { get; set; }
|
||||
}
|
||||
}
|
||||
519
src/Core/PathDatabase.cs
Normal file
519
src/Core/PathDatabase.cs
Normal file
@ -0,0 +1,519 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SQLite;
|
||||
using System.IO;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
namespace NavisworksTransport
|
||||
{
|
||||
/// <summary>
|
||||
/// 路径数据库管理器 - 简化版
|
||||
/// 负责路径和碰撞数据的持久化存储
|
||||
/// </summary>
|
||||
public class PathDatabase : IDisposable
|
||||
{
|
||||
private SQLiteConnection _connection;
|
||||
private readonly string _dbPath;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化路径数据库
|
||||
/// </summary>
|
||||
/// <param name="documentPath">Navisworks文档路径</param>
|
||||
public PathDatabase(string documentPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(documentPath))
|
||||
throw new ArgumentException("文档路径不能为空");
|
||||
|
||||
// 数据库文件与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");
|
||||
|
||||
if (isNewDatabase)
|
||||
{
|
||||
CreateTables();
|
||||
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,
|
||||
CreatedTime DATETIME,
|
||||
LastModified DATETIME
|
||||
)
|
||||
");
|
||||
|
||||
// 2. 碰撞结果表
|
||||
ExecuteNonQuery(@"
|
||||
CREATE TABLE IF NOT EXISTS CollisionResults (
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
RouteId TEXT NOT NULL,
|
||||
Item1Name TEXT,
|
||||
Item2Name TEXT,
|
||||
CenterX REAL,
|
||||
CenterY REAL,
|
||||
CenterZ REAL,
|
||||
Distance REAL,
|
||||
DetectedTime DATETIME,
|
||||
FOREIGN KEY(RouteId) REFERENCES PathRoutes(Id) ON DELETE CASCADE
|
||||
)
|
||||
");
|
||||
|
||||
// 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. 路径点表
|
||||
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,
|
||||
FOREIGN KEY(RouteId) REFERENCES PathRoutes(Id) ON DELETE CASCADE
|
||||
)
|
||||
");
|
||||
|
||||
// 创建索引
|
||||
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_collisions_route ON CollisionResults(RouteId)");
|
||||
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_pathpoints_route ON PathPoints(RouteId)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存路径基本信息
|
||||
/// </summary>
|
||||
public void SavePathRoute(PathRoute route)
|
||||
{
|
||||
// 使用事务确保路径和路径点一起保存
|
||||
using (var transaction = _connection.BeginTransaction())
|
||||
{
|
||||
try
|
||||
{
|
||||
// 保存路径基本信息
|
||||
var sql = @"
|
||||
INSERT OR REPLACE INTO PathRoutes
|
||||
(Id, Name, TotalLength, EstimatedTime, CreatedTime, LastModified)
|
||||
VALUES (@id, @name, @length, @time, @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("@created", route.CreatedTime);
|
||||
cmd.Parameters.AddWithValue("@modified", DateTime.Now);
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
// 删除旧的路径点
|
||||
ExecuteNonQuery("DELETE FROM PathPoints WHERE RouteId=@id", new { id = route.Id });
|
||||
|
||||
// 保存新的路径点
|
||||
if (route.Points != null && route.Points.Count > 0)
|
||||
{
|
||||
var pointSql = @"
|
||||
INSERT INTO PathPoints
|
||||
(Id, RouteId, SequenceNumber, Name, X, Y, Z, Type)
|
||||
VALUES (@id, @routeId, @seq, @name, @x, @y, @z, @type)
|
||||
";
|
||||
|
||||
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.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"保存路径: {route.Name},包含 {route.Points.Count} 个路径点");
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
transaction.Rollback();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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"]),
|
||||
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);
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
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"])
|
||||
};
|
||||
|
||||
route.Points.Add(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"为路径 '{route.Name}' 加载了 {route.Points.Count} 个路径点");
|
||||
}
|
||||
|
||||
/// <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 "未知对象";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放资源
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_connection?.Close();
|
||||
_connection?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <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; }
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,10 @@ namespace NavisworksTransport
|
||||
|
||||
private CoordinateConverter _coordinateConverter;
|
||||
private PathPointRenderPlugin _renderPlugin;
|
||||
|
||||
// 路径分析相关
|
||||
private PathDatabase _pathDatabase;
|
||||
private PathAnalysisService _analysisService;
|
||||
private List<ModelItem> _walkableAreas;
|
||||
private List<PathRoute> _routes;
|
||||
private PathRoute _currentRoute;
|
||||
@ -111,6 +115,40 @@ namespace NavisworksTransport
|
||||
_activePathManager = this;
|
||||
LogManager.WriteLog($"[路径管理] 设置_activePathManager静态引用, ManagerId: {_managerId}");
|
||||
|
||||
// 尝试初始化路径分析数据库
|
||||
EnsureDatabaseInitialized();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保路径分析数据库已初始化(支持延迟初始化)
|
||||
/// </summary>
|
||||
public void EnsureDatabaseInitialized()
|
||||
{
|
||||
if (_pathDatabase != null && _analysisService != null)
|
||||
{
|
||||
LogManager.Info("路径分析数据库已初始化,跳过");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var documentPath = Autodesk.Navisworks.Api.Application.ActiveDocument?.FileName;
|
||||
if (!string.IsNullOrEmpty(documentPath))
|
||||
{
|
||||
_pathDatabase = new PathDatabase(documentPath);
|
||||
_analysisService = new PathAnalysisService(_pathDatabase);
|
||||
LogManager.Info($"路径分析数据库初始化成功,文档路径: {documentPath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Warning("文档路径为空,路径分析数据库初始化将延迟到文档加载后");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"初始化路径分析数据库失败: {ex.Message}", ex);
|
||||
}
|
||||
|
||||
// 获取已注册的圆形渲染插件实例
|
||||
try
|
||||
{
|
||||
@ -119,7 +157,7 @@ namespace NavisworksTransport
|
||||
{
|
||||
LogManager.WriteLog("[路径管理] ✅ PathPointRenderPlugin实例获取成功");
|
||||
LogManager.WriteLog($"[路径管理] 渲染插件状态 - 启用: {_renderPlugin.IsEnabled}, 标记数量: {_renderPlugin.MarkerCount}");
|
||||
|
||||
|
||||
// 推送默认的网格大小和车辆参数,确保渲染插件有合理的初始值
|
||||
InitializeRenderPluginDefaults();
|
||||
}
|
||||
@ -142,6 +180,9 @@ namespace NavisworksTransport
|
||||
_coordinateConverter = null; // 将在需要时初始化
|
||||
_pathPointMarkers = new List<PathPointMarker>();
|
||||
|
||||
// 从数据库加载历史路径到内存
|
||||
LoadHistoricalRoutesFromDatabase();
|
||||
|
||||
LogManager.Info($"PathPlanningManager初始化完成,ManagerId: {_managerId}");
|
||||
}
|
||||
|
||||
@ -163,14 +204,14 @@ namespace NavisworksTransport
|
||||
const double defaultVehicleWidth = 1.0; // 米
|
||||
const double defaultVehicleHeight = 2.0; // 米
|
||||
const double defaultSafetyMargin = 0.25; // 米
|
||||
|
||||
|
||||
_renderPlugin.SetVehicleParameters(
|
||||
defaultVehicleLength,
|
||||
defaultVehicleWidth,
|
||||
defaultVehicleWidth,
|
||||
defaultVehicleHeight,
|
||||
defaultSafetyMargin
|
||||
);
|
||||
|
||||
|
||||
LogManager.Info($"[渲染插件初始化] 已设置默认车辆参数: 长={defaultVehicleLength}m, 宽={defaultVehicleWidth}m, 高={defaultVehicleHeight}m, 安全间隙={defaultSafetyMargin}m");
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -679,10 +720,10 @@ namespace NavisworksTransport
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从3D中移除路径点
|
||||
/// 删除路径点
|
||||
/// </summary>
|
||||
/// <param name="point">要移除的路径点</param>
|
||||
public void RemovePathPointFrom3D(PathPoint point)
|
||||
/// <param name="point">要删除的路径点</param>
|
||||
public void RemovePathPoint(PathPoint point)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -708,6 +749,12 @@ namespace NavisworksTransport
|
||||
|
||||
// 触发路径点移除事件
|
||||
RaisePathPointOperation(PathPointOperationType.Removed, point, CurrentRoute);
|
||||
|
||||
// 保存到数据库
|
||||
if (CurrentRoute != null)
|
||||
{
|
||||
SavePathToDatabase(CurrentRoute);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -991,6 +1038,8 @@ namespace NavisworksTransport
|
||||
if (!_routes.Contains(autoRoute))
|
||||
{
|
||||
_routes.Add(autoRoute);
|
||||
// 保存自动生成的路径到数据库
|
||||
SavePathToDatabase(autoRoute);
|
||||
}
|
||||
CurrentRoute = autoRoute;
|
||||
|
||||
@ -1187,6 +1236,9 @@ namespace NavisworksTransport
|
||||
_routes.Add(route);
|
||||
RaiseStatusChanged($"已添加路径: {route.Name}", PathPlanningStatusType.Success);
|
||||
|
||||
// 保存到数据库
|
||||
SavePathToDatabase(route);
|
||||
|
||||
// AddRoute只负责添加路径到数据集合,不自动设置当前路径
|
||||
// 路径选择应该通过专门的选择逻辑处理,而不是添加操作的副作用
|
||||
// if (_currentRoute == null || _currentRoute.Points.Count == 0)
|
||||
@ -1222,6 +1274,9 @@ namespace NavisworksTransport
|
||||
_routes.Add(newRoute);
|
||||
CurrentRoute = newRoute;
|
||||
|
||||
// 保存到数据库
|
||||
SavePathToDatabase(newRoute);
|
||||
|
||||
RaiseStatusChanged($"已创建新路径: {routeName}", PathPlanningStatusType.Success);
|
||||
return newRoute;
|
||||
}
|
||||
@ -1240,6 +1295,21 @@ namespace NavisworksTransport
|
||||
bool removed = _routes.Remove(route);
|
||||
if (removed)
|
||||
{
|
||||
// 从数据库删除
|
||||
if (_pathDatabase != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_pathDatabase.DeletePathRoute(route.Id);
|
||||
LogManager.Info($"已从数据库删除路径: {route.Name}");
|
||||
}
|
||||
catch (Exception dbEx)
|
||||
{
|
||||
LogManager.Error($"从数据库删除路径失败: {dbEx.Message}", dbEx);
|
||||
// 继续执行,不影响内存操作
|
||||
}
|
||||
}
|
||||
|
||||
if (_currentRoute == route)
|
||||
{
|
||||
var newRoute = _routes.FirstOrDefault() ?? new PathRoute("默认路径");
|
||||
@ -1379,6 +1449,9 @@ namespace NavisworksTransport
|
||||
{
|
||||
_routes.Add(CurrentRoute);
|
||||
|
||||
// 保存到数据库
|
||||
SavePathToDatabase(CurrentRoute);
|
||||
|
||||
// 添加历史记录
|
||||
var historyEntry = new PathHistoryEntry(
|
||||
CurrentRoute.Id,
|
||||
@ -1392,6 +1465,9 @@ namespace NavisworksTransport
|
||||
// 如果是编辑模式,保存编辑历史
|
||||
if (_pathEditState == PathEditState.Editing && EditingRoute != null)
|
||||
{
|
||||
// 更新数据库
|
||||
SavePathToDatabase(EditingRoute);
|
||||
|
||||
var historyEntry = new PathHistoryEntry(
|
||||
EditingRoute.Id,
|
||||
PathHistoryOperationType.Edited,
|
||||
@ -1498,10 +1574,10 @@ namespace NavisworksTransport
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确认修改路径点
|
||||
/// 更新路径点位置(保存修改)
|
||||
/// </summary>
|
||||
/// <returns>是否成功确认修改</returns>
|
||||
public bool ConfirmEditPoint()
|
||||
/// <returns>是否成功更新</returns>
|
||||
public bool UpdatePointPosition()
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -1541,6 +1617,9 @@ namespace NavisworksTransport
|
||||
// 触发路径点列表更新事件
|
||||
RaisePathPointsListUpdated(CurrentRoute, "路径点修改完成");
|
||||
|
||||
// 保存到数据库
|
||||
SavePathToDatabase(CurrentRoute);
|
||||
|
||||
RaiseStatusChanged($"路径点 {pointToUpdate.Name} 修改完成", PathPlanningStatusType.Success);
|
||||
LogManager.Info($"路径点修改完成: {pointToUpdate.Name}");
|
||||
|
||||
@ -1548,7 +1627,7 @@ namespace NavisworksTransport
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RaiseErrorOccurred($"确认修改路径点失败: {ex.Message}", ex);
|
||||
RaiseErrorOccurred($"确认修改路径点失败: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1852,10 +1931,10 @@ namespace NavisworksTransport
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确认预览点,将其添加到当前路径中
|
||||
/// 将预览点添加为正式路径点
|
||||
/// </summary>
|
||||
/// <returns>添加的路径点,如果失败返回null</returns>
|
||||
public PathPoint ConfirmPreviewPoint()
|
||||
public PathPoint ConvertPreviewToPathPoint()
|
||||
{
|
||||
if (!_isPreviewMode || _previewPoint == null)
|
||||
{
|
||||
@ -1913,11 +1992,14 @@ namespace NavisworksTransport
|
||||
// 触发路径列表更新事件
|
||||
RaisePathPointsListUpdated(CurrentRoute, "确认添加预览点");
|
||||
|
||||
// 保存到数据库
|
||||
SavePathToDatabase(CurrentRoute);
|
||||
|
||||
return confirmPoint;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RaiseErrorOccurred($"确认预览点失败: {ex.Message}", ex);
|
||||
RaiseErrorOccurred($"确认预览点失败: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -3482,6 +3564,166 @@ namespace NavisworksTransport
|
||||
|
||||
#endregion
|
||||
|
||||
#region 路径分析方法
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有路径(供分析使用)
|
||||
/// </summary>
|
||||
public List<PathRoute> GetAllRoutes()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 直接返回内存中的路径列表
|
||||
// 因为所有路径操作都已同步到数据库,内存列表就是当前的完整列表
|
||||
return _routes ?? new List<PathRoute>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取所有路径失败: {ex.Message}", ex);
|
||||
return new List<PathRoute>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分析多条路径并进行对比
|
||||
/// </summary>
|
||||
/// <param name="routeIds">要分析的路径ID列表</param>
|
||||
/// <param name="strategy">分析策略</param>
|
||||
/// <returns>对比结果</returns>
|
||||
public PathComparisonResult AnalyzePathsAndCompare(List<string> routeIds, string strategy)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_analysisService == null)
|
||||
{
|
||||
LogManager.Error("路径分析服务未初始化");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取要分析的路径
|
||||
var routesToAnalyze = _routes?.Where(r => routeIds.Contains(r.Id)).ToList();
|
||||
if (routesToAnalyze == null || routesToAnalyze.Count == 0)
|
||||
{
|
||||
LogManager.Warning("未找到要分析的路径");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 执行分析和对比
|
||||
var result = _analysisService.CompareRoutes(routesToAnalyze, strategy);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"路径分析失败: {ex.Message}", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从数据库加载历史路径到内存
|
||||
/// </summary>
|
||||
private void LoadHistoricalRoutesFromDatabase()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_pathDatabase == null)
|
||||
{
|
||||
LogManager.Info("数据库未初始化,跳过加载历史路径");
|
||||
return;
|
||||
}
|
||||
|
||||
var historicalRoutes = _pathDatabase.GetAllPathRoutes();
|
||||
if (historicalRoutes != null && historicalRoutes.Count > 0)
|
||||
{
|
||||
// 使用HashSet记录已存在的路径ID,避免重复
|
||||
var existingIds = new HashSet<string>(_routes.Select(r => r.Id));
|
||||
int loadedCount = 0;
|
||||
|
||||
// 加载历史路径(跳过已存在的)
|
||||
foreach (var route in historicalRoutes)
|
||||
{
|
||||
if (!existingIds.Contains(route.Id))
|
||||
{
|
||||
_routes.Add(route);
|
||||
loadedCount++;
|
||||
LogManager.Info($"加载历史路径: {route.Name}, ID={route.Id}, 长度={route.TotalLength:F2}米");
|
||||
}
|
||||
}
|
||||
|
||||
if (loadedCount > 0)
|
||||
{
|
||||
LogManager.Info($"成功从数据库加载 {loadedCount} 条历史路径(总计 {historicalRoutes.Count} 条)");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info($"数据库中的 {historicalRoutes.Count} 条路径已全部在内存中");
|
||||
}
|
||||
|
||||
// 如果当前没有选中的路径,且路径列表不为空,选择第一条
|
||||
if ((_currentRoute == null || string.IsNullOrEmpty(_currentRoute.Name) || _currentRoute.Name == "默认路径")
|
||||
&& _routes.Count > 0)
|
||||
{
|
||||
_currentRoute = _routes[0];
|
||||
LogManager.Info($"设置当前路径为: {_currentRoute.Name}");
|
||||
}
|
||||
|
||||
// 历史路径加载完成后,触发RouteGenerated事件通知UI刷新
|
||||
// 使用特殊的生成方法标记这是数据库加载
|
||||
foreach (var route in _routes)
|
||||
{
|
||||
RaiseRouteGenerated(route, RouteGenerationMethod.DatabaseLoad);
|
||||
}
|
||||
LogManager.Info($"已触发{_routes.Count}个RouteGenerated事件,通知UI刷新历史路径");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info("数据库中没有历史路径记录");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"加载历史路径失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存路径时同时保存到数据库
|
||||
/// </summary>
|
||||
private void SavePathToDatabase(PathRoute route)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_pathDatabase != null && route != null)
|
||||
{
|
||||
_pathDatabase.SavePathRoute(route);
|
||||
LogManager.Info($"路径已保存到数据库: {route.Name}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"保存路径到数据库失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取Instance单例(为兼容现有代码)
|
||||
/// </summary>
|
||||
public static PathPlanningManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_activePathManager == null)
|
||||
{
|
||||
LogManager.Warning("PathPlanningManager未初始化,创建新实例");
|
||||
_activePathManager = new PathPlanningManager();
|
||||
}
|
||||
return _activePathManager;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 静态方法
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -346,7 +346,12 @@ namespace NavisworksTransport.Core
|
||||
/// <summary>
|
||||
/// 优化
|
||||
/// </summary>
|
||||
Optimization
|
||||
Optimization,
|
||||
|
||||
/// <summary>
|
||||
/// 从数据库加载
|
||||
/// </summary>
|
||||
DatabaseLoad
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -408,6 +408,27 @@ namespace NavisworksTransport
|
||||
/// </summary>
|
||||
public double VehicleHeight { get; set; } = 2.0;
|
||||
|
||||
// 数据库分析相关属性
|
||||
/// <summary>
|
||||
/// 碰撞数量(从数据库加载)
|
||||
/// </summary>
|
||||
public int? CollisionCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 安全评分(从数据库加载)
|
||||
/// </summary>
|
||||
public double? SafetyScore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 效率评分(从数据库加载)
|
||||
/// </summary>
|
||||
public double? EfficiencyScore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 综合评分(从数据库加载)
|
||||
/// </summary>
|
||||
public double? OverallScore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
|
||||
@ -4,7 +4,7 @@ using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using NavisworksTransport.Utils;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
@ -31,7 +31,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
public ObservableCollection<PathAnalysisItem> AvailablePaths { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 选中的路径列表(用于对比分析)
|
||||
/// 选中的路径列表(用于对比分析和结果显示)
|
||||
/// </summary>
|
||||
public ObservableCollection<PathAnalysisItem> SelectedPaths { get; set; }
|
||||
|
||||
@ -99,6 +99,26 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
/// </summary>
|
||||
public ObservableCollection<BottleneckLocation> BottleneckLocations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开始分析命令
|
||||
/// </summary>
|
||||
public ICommand StartAnalysisCommand { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 全选命令
|
||||
/// </summary>
|
||||
public ICommand SelectAllCommand { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 清空选择命令
|
||||
/// </summary>
|
||||
public ICommand ClearSelectionCommand { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 导出报告命令
|
||||
/// </summary>
|
||||
public ICommand ExportReportCommand { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region 构造函数
|
||||
@ -111,7 +131,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
try
|
||||
{
|
||||
InitializeCollections();
|
||||
LoadDemoData();
|
||||
InitializeCommands();
|
||||
|
||||
// 确保PathPlanningManager的数据库已初始化
|
||||
EnsurePathDatabaseInitialized();
|
||||
|
||||
LoadPathData();
|
||||
LogManager.Info("PathAnalysisViewModel初始化完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -120,6 +145,43 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化命令
|
||||
/// </summary>
|
||||
private void InitializeCommands()
|
||||
{
|
||||
StartAnalysisCommand = new RelayCommand(ExecuteStartAnalysis, CanExecuteStartAnalysis);
|
||||
SelectAllCommand = new RelayCommand(SelectAll);
|
||||
ClearSelectionCommand = new RelayCommand(ClearSelection);
|
||||
ExportReportCommand = new RelayCommand(ExportReport, CanExportReport);
|
||||
}
|
||||
|
||||
private bool CanExecuteStartAnalysis()
|
||||
{
|
||||
return !IsAnalyzing && AvailablePaths.Any(p => p.IsSelected);
|
||||
}
|
||||
|
||||
private void ExecuteStartAnalysis()
|
||||
{
|
||||
PerformAnalysis();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保PathPlanningManager的路径数据库已初始化
|
||||
/// </summary>
|
||||
private void EnsurePathDatabaseInitialized()
|
||||
{
|
||||
try
|
||||
{
|
||||
var manager = PathPlanningManager.Instance;
|
||||
manager.EnsureDatabaseInitialized();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"确保数据库初始化失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 初始化方法
|
||||
@ -136,163 +198,109 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载演示数据
|
||||
/// 加载真实路径数据
|
||||
/// </summary>
|
||||
private void LoadDemoData()
|
||||
private void LoadPathData()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 加载演示路径数据
|
||||
var demoPath1 = new PathAnalysisItem
|
||||
// 从PathPlanningManager获取所有路径(包括数据库中的历史路径)
|
||||
var allRoutes = PathPlanningManager.Instance.GetAllRoutes();
|
||||
|
||||
if (allRoutes == null || allRoutes.Count == 0)
|
||||
{
|
||||
Name = "自动路径_20250103_141523",
|
||||
Type = "自动生成",
|
||||
Length = 98.5,
|
||||
EstimatedTime = 147,
|
||||
CollisionCount = 2,
|
||||
AffectedObjects = 5,
|
||||
AverageSpeed = 0.67,
|
||||
Score = 82,
|
||||
IsSelected = true
|
||||
};
|
||||
LogManager.Info("当前没有可用的路径数据");
|
||||
AnalysisStatus = "暂无可用路径数据,请先规划路径";
|
||||
return;
|
||||
}
|
||||
|
||||
var demoPath2 = new PathAnalysisItem
|
||||
// 清空现有数据
|
||||
AvailablePaths.Clear();
|
||||
|
||||
// 将路径数据转换为UI显示项
|
||||
foreach (var route in allRoutes)
|
||||
{
|
||||
Name = "手动路径_20250103_102045",
|
||||
Type = "手动规划",
|
||||
Length = 125.3,
|
||||
EstimatedTime = 188,
|
||||
CollisionCount = 0,
|
||||
AffectedObjects = 3,
|
||||
AverageSpeed = 0.67,
|
||||
Score = 95,
|
||||
IsSelected = true
|
||||
};
|
||||
var item = new PathAnalysisItem
|
||||
{
|
||||
RouteId = route.Id,
|
||||
Name = route.Name,
|
||||
Type = route.CreatedTime != default(DateTime) ? "历史路径" : "当前会话",
|
||||
Length = Math.Round(route.TotalLength, 2),
|
||||
EstimatedTime = (int)route.EstimatedTime,
|
||||
IsSelected = false
|
||||
};
|
||||
|
||||
var demoPath3 = new PathAnalysisItem
|
||||
{
|
||||
Name = "自动路径_20250103_093012",
|
||||
Type = "自动生成",
|
||||
Length = 105.8,
|
||||
EstimatedTime = 159,
|
||||
CollisionCount = 4,
|
||||
AffectedObjects = 8,
|
||||
AverageSpeed = 0.67,
|
||||
Score = 68,
|
||||
IsSelected = false
|
||||
};
|
||||
// 如果数据库中有分析结果,加载相关数据
|
||||
if (route.CollisionCount.HasValue)
|
||||
{
|
||||
item.CollisionCount = route.CollisionCount.Value;
|
||||
item.Score = (int)route.OverallScore.GetValueOrDefault(0);
|
||||
item.AffectedObjects = route.CollisionCount.Value; // 简化:使用碰撞数作为受影响对象数
|
||||
}
|
||||
else
|
||||
{
|
||||
item.CollisionCount = 0;
|
||||
item.Score = 0;
|
||||
item.AffectedObjects = 0;
|
||||
}
|
||||
|
||||
var demoPath4 = new PathAnalysisItem
|
||||
{
|
||||
Name = "手动路径_20250102_160230",
|
||||
Type = "手动规划",
|
||||
Length = 132.6,
|
||||
EstimatedTime = 199,
|
||||
CollisionCount = 1,
|
||||
AffectedObjects = 4,
|
||||
AverageSpeed = 0.67,
|
||||
Score = 78,
|
||||
IsSelected = false
|
||||
};
|
||||
// 计算平均速度
|
||||
if (route.EstimatedTime > 0)
|
||||
{
|
||||
item.AverageSpeed = Math.Round(route.TotalLength / route.EstimatedTime, 2);
|
||||
}
|
||||
|
||||
AvailablePaths.Add(demoPath1);
|
||||
AvailablePaths.Add(demoPath2);
|
||||
AvailablePaths.Add(demoPath3);
|
||||
AvailablePaths.Add(demoPath4);
|
||||
// 监听选择状态变化,以刷新命令状态
|
||||
item.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(PathAnalysisItem.IsSelected))
|
||||
{
|
||||
CommandManager.InvalidateRequerySuggested();
|
||||
}
|
||||
};
|
||||
|
||||
// 添加选中的路径到对比列表
|
||||
SelectedPaths.Add(demoPath1);
|
||||
SelectedPaths.Add(demoPath2);
|
||||
AvailablePaths.Add(item);
|
||||
}
|
||||
|
||||
// 加载优化建议
|
||||
LoadOptimizationSuggestions();
|
||||
|
||||
// 加载瓶颈位置
|
||||
LoadBottleneckLocations();
|
||||
|
||||
// 计算推荐路径
|
||||
UpdateRecommendedPath();
|
||||
|
||||
LogManager.Info("路径分析演示数据加载完成");
|
||||
LogManager.Info($"加载路径数据完成:共{AvailablePaths.Count}条路径(包括历史路径)");
|
||||
AnalysisStatus = $"已加载 {AvailablePaths.Count} 条路径,请选择要分析的路径";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"加载路径分析演示数据失败: {ex.Message}", ex);
|
||||
LogManager.Error($"加载真实路径数据失败: {ex.Message}", ex);
|
||||
AnalysisStatus = "加载路径数据失败";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载优化建议
|
||||
/// </summary>
|
||||
private void LoadOptimizationSuggestions()
|
||||
{
|
||||
OptimizationSuggestions.Add(new OptimizationSuggestion
|
||||
{
|
||||
Title = "在电梯口设置等待区",
|
||||
Description = "避免多路径在电梯处产生拥堵,提高通行效率",
|
||||
Priority = "高",
|
||||
Status = "建议采纳"
|
||||
});
|
||||
|
||||
OptimizationSuggestions.Add(new OptimizationSuggestion
|
||||
{
|
||||
Title = "优化转弯半径至2.0米",
|
||||
Description = "当前1.5米转弯半径过小,增大至2.0米可提高安全性",
|
||||
Priority = "高",
|
||||
Status = "建议采纳"
|
||||
});
|
||||
|
||||
OptimizationSuggestions.Add(new OptimizationSuggestion
|
||||
{
|
||||
Title = "调整作业时间窗口",
|
||||
Description = "建议在09:00-10:00执行,避开人员高峰期",
|
||||
Priority = "中",
|
||||
Status = "可选"
|
||||
});
|
||||
|
||||
OptimizationSuggestions.Add(new OptimizationSuggestion
|
||||
{
|
||||
Title = "考虑分批运输",
|
||||
Description = "单次运输量过大时,可分2批执行降低碰撞风险",
|
||||
Priority = "中",
|
||||
Status = "可选"
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载瓶颈位置
|
||||
/// </summary>
|
||||
private void LoadBottleneckLocations()
|
||||
{
|
||||
BottleneckLocations.Add(new BottleneckLocation
|
||||
{
|
||||
Name = "电梯口",
|
||||
AffectedPaths = 2,
|
||||
Severity = "高风险",
|
||||
Suggestion = "设置等待区"
|
||||
});
|
||||
|
||||
BottleneckLocations.Add(new BottleneckLocation
|
||||
{
|
||||
Name = "通道转角",
|
||||
AffectedPaths = 2,
|
||||
Severity = "中等风险",
|
||||
Suggestion = "优化转弯半径"
|
||||
});
|
||||
|
||||
BottleneckLocations.Add(new BottleneckLocation
|
||||
{
|
||||
Name = "装卸区入口",
|
||||
AffectedPaths = 1,
|
||||
Severity = "低风险",
|
||||
Suggestion = "无需调整"
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 业务逻辑方法
|
||||
|
||||
/// <summary>
|
||||
/// 全选所有路径
|
||||
/// </summary>
|
||||
private void SelectAll()
|
||||
{
|
||||
foreach (var path in AvailablePaths)
|
||||
{
|
||||
path.IsSelected = true;
|
||||
}
|
||||
CommandManager.InvalidateRequerySuggested();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空所有选择
|
||||
/// </summary>
|
||||
private void ClearSelection()
|
||||
{
|
||||
foreach (var path in AvailablePaths)
|
||||
{
|
||||
path.IsSelected = false;
|
||||
}
|
||||
CommandManager.InvalidateRequerySuggested();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新推荐路径
|
||||
/// </summary>
|
||||
@ -348,15 +356,41 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
IsAnalyzing = true;
|
||||
AnalysisStatus = "正在分析...";
|
||||
|
||||
// 模拟分析过程
|
||||
System.Threading.Tasks.Task.Delay(2000).ContinueWith(t =>
|
||||
// 获取选中的路径
|
||||
var selectedRoutes = AvailablePaths.Where(p => p.IsSelected).ToList();
|
||||
if (selectedRoutes.Count == 0)
|
||||
{
|
||||
AnalysisStatus = "请选择要分析的路径";
|
||||
IsAnalyzing = false;
|
||||
UpdateRecommendedPath();
|
||||
AnalysisStatus = "分析完成";
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
LogManager.Info("开始执行路径分析");
|
||||
// 更新选中路径列表用于UI显示
|
||||
SelectedPaths.Clear();
|
||||
foreach (var route in selectedRoutes)
|
||||
{
|
||||
SelectedPaths.Add(route);
|
||||
}
|
||||
|
||||
// 执行真实的路径分析
|
||||
var result = PathPlanningManager.Instance.AnalyzePathsAndCompare(
|
||||
selectedRoutes.Select(p => p.RouteId).ToList(),
|
||||
AnalysisStrategy
|
||||
);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
// 更新UI数据
|
||||
UpdateAnalysisResults(result);
|
||||
AnalysisStatus = "分析完成";
|
||||
LogManager.Info($"路径分析完成,最佳路径: {result.BestRouteName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
AnalysisStatus = "分析失败";
|
||||
}
|
||||
|
||||
IsAnalyzing = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -366,6 +400,143 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出报告
|
||||
/// </summary>
|
||||
private void ExportReport()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (RecommendedPath == null || SelectedPaths.Count == 0)
|
||||
{
|
||||
LogManager.Warning("没有分析结果可以导出");
|
||||
return;
|
||||
}
|
||||
|
||||
// 生成报告文件名
|
||||
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||||
var fileName = $"路径分析报告_{timestamp}.txt";
|
||||
var filePath = System.IO.Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
|
||||
fileName
|
||||
);
|
||||
|
||||
// 生成报告内容
|
||||
var report = new System.Text.StringBuilder();
|
||||
report.AppendLine("====================================");
|
||||
report.AppendLine(" 物流路径分析报告");
|
||||
report.AppendLine("====================================");
|
||||
report.AppendLine();
|
||||
report.AppendLine($"生成时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
||||
report.AppendLine($"分析策略:{AnalysisStrategy}");
|
||||
report.AppendLine();
|
||||
|
||||
report.AppendLine("【分析路径列表】");
|
||||
report.AppendLine("----------------------------------------");
|
||||
foreach (var path in SelectedPaths)
|
||||
{
|
||||
report.AppendLine($"路径名称:{path.Name}");
|
||||
report.AppendLine($" - 长度:{path.Length:F2} 米");
|
||||
report.AppendLine($" - 预估时间:{path.EstimatedTime} 秒");
|
||||
report.AppendLine($" - 碰撞数量:{path.CollisionCount}");
|
||||
report.AppendLine($" - 综合评分:{path.Score}");
|
||||
report.AppendLine();
|
||||
}
|
||||
|
||||
report.AppendLine("【推荐路径】");
|
||||
report.AppendLine("----------------------------------------");
|
||||
if (RecommendedPath != null)
|
||||
{
|
||||
report.AppendLine($"推荐路径:{RecommendedPath.Name}");
|
||||
report.AppendLine($"推荐理由:基于{AnalysisStrategy}策略,该路径具有最优综合评分");
|
||||
report.AppendLine();
|
||||
}
|
||||
|
||||
report.AppendLine("【优化建议】");
|
||||
report.AppendLine("----------------------------------------");
|
||||
if (OptimizationSuggestions.Count > 0)
|
||||
{
|
||||
int index = 1;
|
||||
foreach (var suggestion in OptimizationSuggestions)
|
||||
{
|
||||
report.AppendLine($"{index}. {suggestion.Title}");
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
report.AppendLine("暂无优化建议");
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
System.IO.File.WriteAllText(filePath, report.ToString(), System.Text.Encoding.UTF8);
|
||||
|
||||
AnalysisStatus = $"报告已导出到:{filePath}";
|
||||
LogManager.Info($"分析报告已导出:{filePath}");
|
||||
|
||||
// 打开文件所在目录并选中文件
|
||||
System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{filePath}\"");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"导出报告失败:{ex.Message}", ex);
|
||||
AnalysisStatus = "导出报告失败";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否可以导出报告
|
||||
/// </summary>
|
||||
private bool CanExportReport()
|
||||
{
|
||||
return SelectedPaths != null && SelectedPaths.Count > 0 && RecommendedPath != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新分析结果到UI
|
||||
/// </summary>
|
||||
private void UpdateAnalysisResults(PathComparisonResult result)
|
||||
{
|
||||
// 更新路径的碰撞和评分数据
|
||||
foreach (var analysis in result.Analyses)
|
||||
{
|
||||
// 更新AvailablePaths中的数据
|
||||
var pathItem = AvailablePaths.FirstOrDefault(p => p.RouteId == analysis.RouteId);
|
||||
if (pathItem != null)
|
||||
{
|
||||
pathItem.CollisionCount = analysis.CollisionCount;
|
||||
pathItem.Score = (int)analysis.OverallScore;
|
||||
pathItem.AverageSpeed = pathItem.Length / pathItem.EstimatedTime;
|
||||
pathItem.AffectedObjects = analysis.CollisionCount; // 简化:使用碰撞数作为受影响对象数
|
||||
}
|
||||
|
||||
// 同时更新SelectedPaths中的数据
|
||||
var selectedItem = SelectedPaths.FirstOrDefault(p => p.RouteId == analysis.RouteId);
|
||||
if (selectedItem != null)
|
||||
{
|
||||
selectedItem.CollisionCount = analysis.CollisionCount;
|
||||
selectedItem.Score = (int)analysis.OverallScore;
|
||||
selectedItem.AverageSpeed = selectedItem.Length / selectedItem.EstimatedTime;
|
||||
selectedItem.AffectedObjects = analysis.CollisionCount;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置推荐路径
|
||||
RecommendedPath = SelectedPaths.FirstOrDefault(p => p.RouteId == result.BestRouteId);
|
||||
|
||||
// 更新优化建议
|
||||
OptimizationSuggestions.Clear();
|
||||
foreach (var suggestion in result.Suggestions)
|
||||
{
|
||||
OptimizationSuggestions.Add(new OptimizationSuggestion
|
||||
{
|
||||
Title = suggestion,
|
||||
Priority = "中",
|
||||
Status = "待处理"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INotifyPropertyChanged实现
|
||||
@ -385,8 +556,17 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
/// <summary>
|
||||
/// 路径分析项目
|
||||
/// </summary>
|
||||
public class PathAnalysisItem
|
||||
public class PathAnalysisItem : INotifyPropertyChanged
|
||||
{
|
||||
private bool _isSelected;
|
||||
private int _collisionCount;
|
||||
private int _affectedObjects;
|
||||
private double _averageSpeed;
|
||||
private int _score;
|
||||
|
||||
/// <summary>路径ID</summary>
|
||||
public string RouteId { get; set; }
|
||||
|
||||
/// <summary>路径名称</summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
@ -400,19 +580,66 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
public int EstimatedTime { get; set; }
|
||||
|
||||
/// <summary>碰撞次数</summary>
|
||||
public int CollisionCount { get; set; }
|
||||
public int CollisionCount
|
||||
{
|
||||
get => _collisionCount;
|
||||
set
|
||||
{
|
||||
_collisionCount = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>受影响对象数</summary>
|
||||
public int AffectedObjects { get; set; }
|
||||
public int AffectedObjects
|
||||
{
|
||||
get => _affectedObjects;
|
||||
set
|
||||
{
|
||||
_affectedObjects = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>平均速度(m/s)</summary>
|
||||
public double AverageSpeed { get; set; }
|
||||
public double AverageSpeed
|
||||
{
|
||||
get => _averageSpeed;
|
||||
set
|
||||
{
|
||||
_averageSpeed = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>综合评分</summary>
|
||||
public int Score { get; set; }
|
||||
public int Score
|
||||
{
|
||||
get => _score;
|
||||
set
|
||||
{
|
||||
_score = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>是否被选中用于分析</summary>
|
||||
public bool IsSelected { get; set; }
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
set
|
||||
{
|
||||
_isSelected = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -1174,7 +1174,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
if (isEditingPoint)
|
||||
{
|
||||
// 修改路径点模式 - 尝试确认修改
|
||||
success = _pathPlanningManager.ConfirmEditPoint();
|
||||
success = _pathPlanningManager.UpdatePointPosition();
|
||||
if (success)
|
||||
{
|
||||
operationMessage = "路径点修改已确认";
|
||||
@ -1195,7 +1195,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
if (wasInPreviewMode)
|
||||
{
|
||||
// 预览模式 - 先确认预览点,然后立即完成编辑
|
||||
var confirmedPoint = _pathPlanningManager.ConfirmPreviewPoint();
|
||||
var confirmedPoint = _pathPlanningManager.ConvertPreviewToPathPoint();
|
||||
if (confirmedPoint != null)
|
||||
{
|
||||
LogManager.Info($"[UI-预览模式] 预览点已确认: {confirmedPoint.Name},现在完成编辑");
|
||||
@ -1344,7 +1344,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
LogManager.Info($"已从Core数据删除路径点: {corePoint.Name}");
|
||||
|
||||
// 调用PathPlanningManager的3D删除方法
|
||||
_pathPlanningManager.RemovePathPointFrom3D(corePoint);
|
||||
_pathPlanningManager.RemovePathPoint(corePoint);
|
||||
|
||||
// 触发UI同步更新事件
|
||||
_pathPlanningManager.RaisePathPointsListUpdated(coreRoute, "删除路径点并重新分配类型");
|
||||
@ -2209,7 +2209,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
_pathPlanningManager.ErrorOccurred += OnPathPlanningError;
|
||||
|
||||
LogManager.Info("*** PathEditingViewModel已成功订阅PathPlanningManager事件,包括RouteGenerated和ErrorOccurred ***");
|
||||
|
||||
|
||||
// 验证事件订阅是否成功
|
||||
var routeGeneratedField = _pathPlanningManager.GetType().GetEvent("RouteGenerated");
|
||||
if (routeGeneratedField != null)
|
||||
@ -2220,6 +2220,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
LogManager.Error("*** RouteGenerated事件不存在!!! ***");
|
||||
}
|
||||
|
||||
// 初始化加载现有路径(包括从数据库加载的历史路径)
|
||||
RefreshPathRoutes();
|
||||
LogManager.Info("*** 已从PathPlanningManager加载现有路径到UI ***");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -2604,6 +2608,49 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 手工路径生成(如果需要特殊处理)
|
||||
LogManager.Info($"手工路径生成完成: {e.Route.Name}");
|
||||
}
|
||||
else if (e.GenerationMethod == RouteGenerationMethod.DatabaseLoad)
|
||||
{
|
||||
// 从数据库加载的历史路径
|
||||
LogManager.Info($"*** 开始处理数据库加载路径: {e.Route.Name} ***");
|
||||
|
||||
// 创建对应的 WPF ViewModel
|
||||
var dbPathViewModel = new PathRouteViewModel
|
||||
{
|
||||
Name = e.Route.Name,
|
||||
Description = e.Route.Description,
|
||||
IsActive = false // 历史路径默认不激活
|
||||
};
|
||||
|
||||
// 转换路径点
|
||||
foreach (var corePoint in e.Route.Points)
|
||||
{
|
||||
var wpfPoint = new PathPointViewModel
|
||||
{
|
||||
Id = corePoint.Id,
|
||||
Name = corePoint.Name,
|
||||
X = corePoint.X,
|
||||
Y = corePoint.Y,
|
||||
Z = corePoint.Z,
|
||||
Type = corePoint.Type
|
||||
};
|
||||
dbPathViewModel.Points.Add(wpfPoint);
|
||||
}
|
||||
|
||||
LogManager.Info($"*** 已创建PathRouteViewModel(数据库路径),包含 {dbPathViewModel.Points.Count} 个点 ***");
|
||||
|
||||
// 检查是否已存在同名路径,避免重复添加
|
||||
var existingPath = PathRoutes.FirstOrDefault(p => p.Name == e.Route.Name);
|
||||
if (existingPath == null)
|
||||
{
|
||||
// 添加到 UI 列表
|
||||
PathRoutes.Add(dbPathViewModel);
|
||||
LogManager.Info($"*** 数据库路径已添加到UI列表: {e.Route.Name},当前PathRoutes.Count = {PathRoutes.Count} ***");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info($"*** 数据库路径已存在,跳过: {e.Route.Name} ***");
|
||||
}
|
||||
}
|
||||
}, "处理路径生成事件");
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@ -161,7 +161,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
if (route == null)
|
||||
{
|
||||
LogManager.Info("路径为空,加载演示数据");
|
||||
LoadDemoData();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -173,7 +172,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
if (!result.IsSuccessful)
|
||||
{
|
||||
LogManager.Warning($"时标计算失败: {result.ErrorMessage},回退到演示数据");
|
||||
LoadDemoData();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -211,8 +209,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"加载路径时间数据失败: {ex.Message}", ex);
|
||||
// 加载失败时回退到演示数据
|
||||
LoadDemoData();
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,48 +244,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载演示数据
|
||||
/// </summary>
|
||||
private void LoadDemoData()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 加载时间分段演示数据
|
||||
TimeSegments.Add(new TimeSegmentItem
|
||||
{
|
||||
Index = 1,
|
||||
Length = 9.70,
|
||||
Speed = 0.80,
|
||||
Duration = 12.1,
|
||||
CumulativeTime = 12.1,
|
||||
Remarks = "通道限速"
|
||||
});
|
||||
|
||||
TimeSegments.Add(new TimeSegmentItem
|
||||
{
|
||||
Index = 2,
|
||||
Length = 6.40,
|
||||
Speed = 0.70,
|
||||
Duration = 10.7,
|
||||
CumulativeTime = 22.8,
|
||||
Remarks = "拐角减速"
|
||||
});
|
||||
|
||||
// 移除硬编码的等待时间,使用真实的路径数据
|
||||
|
||||
// 更新总时长并生成时间轴预览数据
|
||||
TotalDuration = 22.8; // 只计算实际路径时间
|
||||
GenerateTimelineItems();
|
||||
|
||||
LogManager.Info("时间标签演示数据加载完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"加载时间标签演示数据失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 业务逻辑方法
|
||||
|
||||
@ -118,82 +118,59 @@ NavisworksTransport 路径规划分析对话框 - 采用与主界面一致的Nav
|
||||
|
||||
<!-- 路径列表 -->
|
||||
<ScrollViewer Height="200" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel>
|
||||
<!-- 路径项1 -->
|
||||
<Border Background="#FFF8FBFF" BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="3" Padding="8" Margin="0,2">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox Grid.Column="0" IsChecked="True" VerticalAlignment="Center" Margin="0,0,8,0"/>
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Text="自动路径_20250103_141523" FontWeight="SemiBold" FontSize="11"/>
|
||||
<TextBlock Text="长度: 98.5m | 时间: 147s" FontSize="9" Foreground="#FF666666"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 路径项2 -->
|
||||
<Border Background="#FFF8FBFF" BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="3" Padding="8" Margin="0,2">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox Grid.Column="0" IsChecked="True" VerticalAlignment="Center" Margin="0,0,8,0"/>
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Text="手动路径_20250103_102045" FontWeight="SemiBold" FontSize="11"/>
|
||||
<TextBlock Text="长度: 125.3m | 时间: 188s" FontSize="9" Foreground="#FF666666"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 路径项3 -->
|
||||
<Border Background="#FFF8FBFF" BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="3" Padding="8" Margin="0,2">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox Grid.Column="0" IsChecked="False" VerticalAlignment="Center" Margin="0,0,8,0"/>
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Text="自动路径_20250103_093012" FontWeight="SemiBold" FontSize="11"/>
|
||||
<TextBlock Text="长度: 105.8m | 时间: 159s" FontSize="9" Foreground="#FF666666"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 路径项4 -->
|
||||
<Border Background="#FFF8FBFF" BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="3" Padding="8" Margin="0,2">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox Grid.Column="0" IsChecked="False" VerticalAlignment="Center" Margin="0,0,8,0"/>
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Text="手动路径_20250102_160230" FontWeight="SemiBold" FontSize="11"/>
|
||||
<TextBlock Text="长度: 132.6m | 时间: 199s" FontSize="9" Foreground="#FF666666"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
<ItemsControl ItemsSource="{Binding AvailablePaths}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="#FFF8FBFF" BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="3" Padding="8" Margin="0,2">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox Grid.Column="0" IsChecked="{Binding IsSelected, Mode=TwoWay}" VerticalAlignment="Center" Margin="0,0,8,0"/>
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" FontSize="11"/>
|
||||
<TextBlock FontSize="9" Foreground="#FF666666">
|
||||
<Run Text="长度: "/>
|
||||
<Run Text="{Binding Length, StringFormat={}{0:F1}m}"/>
|
||||
<Run Text=" | 时间: "/>
|
||||
<Run Text="{Binding EstimatedTime}"/>
|
||||
<Run Text="s"/>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,15,0,0">
|
||||
<Button Content="全选" Style="{StaticResource SecondaryButtonStyle}" Width="60" Margin="0,0,8,0"/>
|
||||
<Button Content="清空" Style="{StaticResource SecondaryButtonStyle}" Width="60"/>
|
||||
<Button Content="全选" Command="{Binding SelectAllCommand}" Style="{StaticResource SecondaryButtonStyle}" Width="60" Margin="0,0,8,0"/>
|
||||
<Button Content="清空" Command="{Binding ClearSelectionCommand}" Style="{StaticResource SecondaryButtonStyle}" Width="60"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 分析策略 -->
|
||||
<TextBlock Text="🎯 分析策略" Style="{StaticResource SectionTitleStyle}" Margin="0,20,0,10"/>
|
||||
<RadioButton Content="安全优先(避开碰撞)" IsChecked="True" Margin="0,3"/>
|
||||
<RadioButton Content="效率优先(最短路径)" Margin="0,3"/>
|
||||
|
||||
<RadioButton Content="安全优先(避开碰撞)"
|
||||
GroupName="AnalysisStrategy"
|
||||
Tag="安全优先"
|
||||
Checked="StrategyRadioButton_Checked"
|
||||
IsChecked="True"
|
||||
Margin="0,3"/>
|
||||
<RadioButton Content="效率优先(最短路径)"
|
||||
GroupName="AnalysisStrategy"
|
||||
Tag="效率优先"
|
||||
Checked="StrategyRadioButton_Checked"
|
||||
Margin="0,3"/>
|
||||
|
||||
<!-- 分析按钮 -->
|
||||
<Button Content="开始分析" Style="{StaticResource ActionButtonStyle}" Height="35" Margin="0,20,0,0"/>
|
||||
<Button Content="开始分析"
|
||||
Command="{Binding StartAnalysisCommand}"
|
||||
Style="{StaticResource ActionButtonStyle}"
|
||||
Height="35"
|
||||
Margin="0,20,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
@ -204,103 +181,50 @@ NavisworksTransport 路径规划分析对话框 - 采用与主界面一致的Nav
|
||||
<TextBlock Text="📊 对比分析" Style="{StaticResource SectionTitleStyle}"/>
|
||||
|
||||
<!-- 对比表格 -->
|
||||
<DataGrid AutoGenerateColumns="False" HeadersVisibility="Column" CanUserReorderColumns="False"
|
||||
<DataGrid ItemsSource="{Binding SelectedPaths}"
|
||||
AutoGenerateColumns="False" HeadersVisibility="Column" CanUserReorderColumns="False"
|
||||
CanUserResizeColumns="True" CanUserSortColumns="False" IsReadOnly="True"
|
||||
BorderBrush="#FFD4E7FF" BorderThickness="1" GridLinesVisibility="Horizontal"
|
||||
HorizontalGridLinesBrush="#FFEEEEEE" RowBackground="White" AlternatingRowBackground="#FFF8FBFF"
|
||||
Height="120">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="路径名称" Width="180"/>
|
||||
<DataGridTextColumn Header="长度(m)" Width="80"/>
|
||||
<DataGridTextColumn Header="时间(s)" Width="80"/>
|
||||
<DataGridTextColumn Header="碰撞" Width="60"/>
|
||||
<DataGridTextColumn Header="影响对象" Width="80"/>
|
||||
<DataGridTextColumn Header="路径名称" Binding="{Binding Name}" Width="180"/>
|
||||
<DataGridTextColumn Header="长度(m)" Binding="{Binding Length, StringFormat={}{0:F1}}" Width="80"/>
|
||||
<DataGridTextColumn Header="时间(s)" Binding="{Binding EstimatedTime}" Width="80"/>
|
||||
<DataGridTextColumn Header="碰撞" Binding="{Binding CollisionCount}" Width="60"/>
|
||||
<DataGridTextColumn Header="评分" Binding="{Binding Score}" Width="60"/>
|
||||
</DataGrid.Columns>
|
||||
<!-- 静态演示数据,实际使用时绑定到ViewModel -->
|
||||
</DataGrid>
|
||||
|
||||
<!-- 图表展示区 -->
|
||||
<TextBlock Text="📈 可视化对比" Style="{StaticResource SectionTitleStyle}" Margin="0,25,0,15"/>
|
||||
|
||||
<!-- 路径长度对比 -->
|
||||
<StackPanel Margin="0,0,0,15">
|
||||
<TextBlock Text="路径长度对比 (米)" FontWeight="SemiBold" FontSize="12" Margin="0,0,0,8"/>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="35"/>
|
||||
<RowDefinition Height="35"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="140"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="60"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="自动路径_141523" Style="{StaticResource MetricLabelStyle}"/>
|
||||
<Rectangle Grid.Column="1" Style="{StaticResource ChartBarStyle}" Width="160" HorizontalAlignment="Left"/>
|
||||
<TextBlock Grid.Column="2" Text="98.5m" Style="{StaticResource MetricValueStyle}" FontSize="11"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="140"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="60"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="手动路径_102045" Style="{StaticResource MetricLabelStyle}"/>
|
||||
<Rectangle Grid.Column="1" Style="{StaticResource ChartBarStyle}" Width="200" HorizontalAlignment="Left" Fill="#FF28A745"/>
|
||||
<TextBlock Grid.Column="2" Text="125.3m" Style="{StaticResource MetricValueStyle}" FontSize="11"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 碰撞次数对比 -->
|
||||
<StackPanel Margin="0,0,0,15">
|
||||
<TextBlock Text="碰撞风险对比 (次数)" FontWeight="SemiBold" FontSize="12" Margin="0,0,0,8"/>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="35"/>
|
||||
<RowDefinition Height="35"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="140"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="60"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="自动路径_141523" Style="{StaticResource MetricLabelStyle}"/>
|
||||
<Rectangle Grid.Column="1" Style="{StaticResource ChartBarStyle}" Width="40" HorizontalAlignment="Left" Fill="#FFFF6B35"/>
|
||||
<TextBlock Grid.Column="2" Text="2次" Style="{StaticResource MetricValueStyle}" FontSize="11"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="140"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="60"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="手动路径_102045" Style="{StaticResource MetricLabelStyle}"/>
|
||||
<Rectangle Grid.Column="1" Style="{StaticResource ChartBarStyle}" Width="0" HorizontalAlignment="Left" Fill="#FF28A745"/>
|
||||
<TextBlock Grid.Column="2" Text="0次" Style="{StaticResource MetricValueStyle}" FontSize="11" Foreground="#FF28A745"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 关键瓶颈位置 -->
|
||||
<TextBlock Text="⚠️ 关键瓶颈位置" FontWeight="SemiBold" FontSize="12" Margin="0,15,0,8"/>
|
||||
<ItemsControl>
|
||||
<Border Background="#FFFFE6E6" BorderBrush="#FFFF9999" BorderThickness="1" CornerRadius="3" Padding="8" Margin="0,2">
|
||||
<TextBlock Text="📍 电梯口 - 影响2条路径,建议设置等待区" FontSize="10"/>
|
||||
</Border>
|
||||
<Border Background="#FFFFF3E6" BorderBrush="#FFFFCC99" BorderThickness="1" CornerRadius="3" Padding="8" Margin="0,2">
|
||||
<TextBlock Text="📍 通道转角 - 转弯半径不足,建议优化" FontSize="10"/>
|
||||
</Border>
|
||||
<Border Background="#FFF0F8FF" BorderBrush="#FFB0D4F1" BorderThickness="1" CornerRadius="3" Padding="8" Margin="0,2">
|
||||
<TextBlock Text="📍 装卸区入口 - 通行良好,无需调整" FontSize="10"/>
|
||||
</Border>
|
||||
</ItemsControl>
|
||||
<!-- 推荐路径 -->
|
||||
<TextBlock Text="🏆 推荐路径" Style="{StaticResource SectionTitleStyle}" Margin="0,25,0,15"/>
|
||||
<Border Background="#FFF0F8FF" BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="3" Padding="15" Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" FontWeight="SemiBold" Foreground="#FF2C3E50">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding StringFormat="推荐路径: {0}">
|
||||
<Binding Path="RecommendedPath.Name" FallbackValue="未选择"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="11" Foreground="#FF7F8C8D" Margin="0,5,0,0">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding StringFormat="长度: {0:F1}米 | 时间: {1}秒 | 碰撞: {2}次 | 评分: {3}">
|
||||
<Binding Path="RecommendedPath.Length" FallbackValue="0"/>
|
||||
<Binding Path="RecommendedPath.EstimatedTime" FallbackValue="0"/>
|
||||
<Binding Path="RecommendedPath.CollisionCount" FallbackValue="0"/>
|
||||
<Binding Path="RecommendedPath.Score" FallbackValue="0"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 分析状态 -->
|
||||
<TextBlock Text="📋 分析状态" Style="{StaticResource SectionTitleStyle}" Margin="0,15,0,10"/>
|
||||
<Border Background="#FFFAFAFA" BorderBrush="#FFE0E0E0" BorderThickness="1" CornerRadius="3" Padding="10">
|
||||
<TextBlock Text="{Binding AnalysisStatus}" FontSize="12" Foreground="#FF666666"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
@ -310,69 +234,25 @@ NavisworksTransport 路径规划分析对话框 - 采用与主界面一致的Nav
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel>
|
||||
<TextBlock Text="🏆 分析结果" Style="{StaticResource SectionTitleStyle}"/>
|
||||
|
||||
<!-- 最佳路径推荐 -->
|
||||
<Border Style="{StaticResource BestPathStyle}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="✅ 推荐方案" FontWeight="Bold" FontSize="13" Foreground="#FF28A745" Margin="0,0,0,8"/>
|
||||
<TextBlock Text="手动路径_20250103_102045" FontWeight="SemiBold" FontSize="12" Margin="0,0,0,5"/>
|
||||
<TextBlock Text="• 零碰撞风险" FontSize="10" Margin="0,2"/>
|
||||
<TextBlock Text="• 受影响对象最少" FontSize="10" Margin="0,2"/>
|
||||
<TextBlock Text="• 符合安全优先策略" FontSize="10" Margin="0,2"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 性能评分 -->
|
||||
<TextBlock Text="📊 综合评分" FontWeight="SemiBold" FontSize="12" Margin="0,20,0,10"/>
|
||||
<StackPanel>
|
||||
<Border Background="#FFF8FBFF" BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="3" Padding="8" Margin="0,2">
|
||||
<Grid>
|
||||
<TextBlock Text="手动路径_102045" HorizontalAlignment="Left" FontSize="10"/>
|
||||
<TextBlock Text="95分 🌟" HorizontalAlignment="Right" FontWeight="Bold" FontSize="10" Foreground="#FF28A745"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border Background="#FFF8FBFF" BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="3" Padding="8" Margin="0,2">
|
||||
<Grid>
|
||||
<TextBlock Text="自动路径_141523" HorizontalAlignment="Left" FontSize="10"/>
|
||||
<TextBlock Text="82分" HorizontalAlignment="Right" FontWeight="Bold" FontSize="10" Foreground="#FF2B579A"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 调整建议 -->
|
||||
|
||||
<!-- 优化建议 -->
|
||||
<TextBlock Text="💡 优化建议" FontWeight="SemiBold" FontSize="12" Margin="0,20,0,10"/>
|
||||
<StackPanel>
|
||||
<Border Style="{StaticResource RecommendationItemStyle}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="✅ 在电梯口设置等待区" FontWeight="SemiBold" FontSize="10" Margin="0,0,0,3"/>
|
||||
<TextBlock Text="避免多路径在电梯处产生拥堵,提高通行效率" FontSize="9" Foreground="#FF666666" TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Style="{StaticResource RecommendationItemStyle}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="✅ 优化转弯半径至2.0米" FontWeight="SemiBold" FontSize="10" Margin="0,0,0,3"/>
|
||||
<TextBlock Text="当前1.5米转弯半径过小,增大至2.0米可提高安全性" FontSize="9" Foreground="#FF666666" TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Style="{StaticResource RecommendationItemStyle}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="⚠️ 调整作业时间窗口" FontWeight="SemiBold" FontSize="10" Margin="0,0,0,3"/>
|
||||
<TextBlock Text="建议在09:00-10:00执行,避开人员高峰期" FontSize="9" Foreground="#FF666666" TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Style="{StaticResource RecommendationItemStyle}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="⚠️ 考虑分批运输" FontWeight="SemiBold" FontSize="10" Margin="0,0,0,3"/>
|
||||
<TextBlock Text="单次运输量过大时,可分2批执行降低碰撞风险" FontSize="9" Foreground="#FF666666" TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
<ItemsControl ItemsSource="{Binding OptimizationSuggestions}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Style="{StaticResource RecommendationItemStyle}">
|
||||
<TextBlock Text="{Binding Title}" FontSize="10" TextWrapping="Wrap"/>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- 导出按钮 -->
|
||||
<Button Content="导出分析报告" Style="{StaticResource ActionButtonStyle}" Height="35" Margin="0,20,0,0"/>
|
||||
<Button Content="导出分析报告"
|
||||
Command="{Binding ExportReportCommand}"
|
||||
Style="{StaticResource ActionButtonStyle}"
|
||||
Height="35"
|
||||
Margin="0,20,0,0"/>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using NavisworksTransport.UI.WPF.ViewModels;
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.Views
|
||||
@ -18,39 +20,36 @@ namespace NavisworksTransport.UI.WPF.Views
|
||||
try
|
||||
{
|
||||
InitializeComponent();
|
||||
LogManager.Info("PathAnalysisDialog初始化完成");
|
||||
|
||||
|
||||
// 绑定ViewModel
|
||||
DataContext = new ViewModels.PathAnalysisViewModel();
|
||||
|
||||
LogManager.Info("PathAnalysisDialog初始化完成,已绑定PathAnalysisViewModel");
|
||||
|
||||
// 设置窗口标题包含当前时间
|
||||
Title = $"路径规划分析 - 多路径对比 [{DateTime.Now:MM-dd HH:mm}]";
|
||||
|
||||
// 加载演示数据(后续可以替换为实际数据绑定)
|
||||
LoadDemoData();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"初始化PathAnalysisDialog失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 加载演示数据到UI控件
|
||||
/// 这里提供静态演示数据,实际使用时应该绑定到ViewModel
|
||||
/// 分析策略 RadioButton 选中事件
|
||||
/// </summary>
|
||||
private void LoadDemoData()
|
||||
private void StrategyRadioButton_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
if (sender is RadioButton rb && rb.Tag is string strategy)
|
||||
{
|
||||
// 这里可以设置一些初始化的演示数据
|
||||
// 目前UI已经包含了静态的演示数据,后续可以通过DataContext绑定动态数据
|
||||
|
||||
LogManager.Info("路径分析演示数据加载完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"加载路径分析演示数据失败: {ex.Message}", ex);
|
||||
if (DataContext is PathAnalysisViewModel viewModel)
|
||||
{
|
||||
viewModel.AnalysisStrategy = strategy;
|
||||
LogManager.Info($"用户切换分析策略: {strategy}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 关闭按钮点击事件
|
||||
/// </summary>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user