实现了基本的路径分析功能,增加了文档关联的sqllite数据库

This commit is contained in:
tian 2025-10-03 16:28:02 +08:00
parent 4357b91446
commit 6091b794de
16 changed files with 1680 additions and 457 deletions

View File

@ -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": [

View File

@ -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
View 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

View File

@ -2,6 +2,10 @@
## 功能点
### [2025/10/01]
1. [x] (功能)路径分析
### [2025/09/29]
1. [x] (功能)导出导航地图为图片

View File

@ -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>

View File

@ -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>

View 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
View 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; }
}
}

View File

@ -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>

View File

@ -346,7 +346,12 @@ namespace NavisworksTransport.Core
/// <summary>
/// 优化
/// </summary>
Optimization
Optimization,
/// <summary>
/// 从数据库加载
/// </summary>
DatabaseLoad
}
#endregion

View File

@ -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>

View File

@ -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>

View File

@ -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)

View File

@ -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

View File

@ -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>

View File

@ -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>