NavisworksTransport/src/Core/PathPlanningModels.cs

2766 lines
89 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using Autodesk.Navisworks.Api;
using System.Text;
using System.Linq;
using System.IO;
using NavisworksTransport.Utils;
using NavisworksTransport.Core.Config;
namespace NavisworksTransport
{
/// <summary>
/// 路径编辑状态枚举
/// </summary>
public enum PathEditState
{
/// <summary>
/// 无状态 - 默认状态
/// </summary>
None,
/// <summary>
/// 查看状态 - 只能查看路径,不能编辑
/// </summary>
Viewing,
/// <summary>
/// 新建状态 - 正在创建新路径
/// </summary>
Creating,
/// <summary>
/// 编辑状态 - 正在编辑现有路径
/// </summary>
Editing,
/// <summary>
/// 添加路径点状态 - 正在添加路径点
/// </summary>
AddingPoints,
/// <summary>
/// 修改路径点状态 - 正在修改现有路径点
/// </summary>
EditingPoint
}
/// <summary>
/// 路径类型枚举 - 分离空轨和吊装为独立类型
/// </summary>
public enum PathType
{
/// <summary>
/// 地面路径 - 物体在地面运行
/// </summary>
Ground = 0,
/// <summary>
/// 空轨路径 - 物体沿空轨移动(可能有坡度),可以获取中心线并吸附
/// </summary>
Rail = 1,
/// <summary>
/// 吊装路径 - 起点吊起 → 平移 → 终点吊下
/// </summary>
Hoisting = 2
}
/// <summary>
/// PathType枚举扩展方法
/// </summary>
public static class PathTypeExtensions
{
/// <summary>
/// 获取路径类型的中文名称
/// </summary>
public static string GetDisplayName(this PathType pathType)
{
switch (pathType)
{
case PathType.Ground:
return "地面";
case PathType.Rail:
return "空轨";
case PathType.Hoisting:
return "吊装";
default:
return "未知";
}
}
}
/// <summary>
/// 吊装路径点方向类型
/// 用于标识吊装路径中每个路径点的移动方向
/// </summary>
public enum HoistingPointDirection
{
/// <summary>
/// 垂直移动(起吊/下降)
/// </summary>
Vertical,
/// <summary>
/// 纵向移动X轴方向
/// </summary>
Longitudinal,
/// <summary>
/// 横向移动Y轴方向
/// </summary>
Lateral
}
/// <summary>
/// Rail 路径安装方式
/// </summary>
public enum RailMountMode
{
/// <summary>
/// 轨下安装
/// </summary>
UnderRail = 0,
/// <summary>
/// 轨上安装
/// </summary>
OverRail = 1
}
/// <summary>
/// Rail 路径的参考线定义方式
/// </summary>
public enum RailPathDefinitionMode
{
/// <summary>
/// 旧版兼容模式:路径点即轨下悬挂参考点
/// </summary>
LegacySuspensionPoint = 0,
/// <summary>
/// 新版模式:路径点表示双轨参考中心线
/// </summary>
RailCenterLine = 1
}
/// <summary>
/// 通道检测结果
/// </summary>
public class ChannelDetectionResult
{
/// <summary>
/// 是否为有效位置
/// </summary>
public bool IsValidLocation { get; set; }
/// <summary>
/// 检测消息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 检测方法
/// </summary>
public string DetectionMethod { get; set; }
}
/// <summary>
/// 物流通道检测结果
/// </summary>
public class LogisticsChannelDetectionResult
{
/// <summary>
/// 是否为有效通道
/// </summary>
public bool IsValidChannel { get; set; }
/// <summary>
/// 通道类型名称
/// </summary>
public string ChannelType { get; set; }
/// <summary>
/// 检测到的通道模型项
/// </summary>
public ModelItem DetectedChannel { get; set; }
/// <summary>
/// 物流类别
/// </summary>
public CategoryAttributeManager.LogisticsElementType? LogisticsCategory { get; set; }
/// <summary>
/// 检测置信度
/// </summary>
public double DetectionConfidence { get; set; }
/// <summary>
/// 错误消息
/// </summary>
public string ErrorMessage { get; set; }
}
/// <summary>
/// 几何分析结果
/// </summary>
public class GeometryAnalysisResult
{
/// <summary>
/// 是否可能是通道
/// </summary>
public bool IsLikelyChannel { get; set; }
/// <summary>
/// 分析原因
/// </summary>
public string Reason { get; set; }
/// <summary>
/// 最可能的通道模型项
/// </summary>
public ModelItem MostLikelyChannel { get; set; }
/// <summary>
/// 置信度
/// </summary>
public double Confidence { get; set; }
}
/// <summary>
/// 路径规划策略枚举
/// </summary>
public enum PathStrategy
{
/// <summary>
/// 最短路径 - 标准A*算法,所有边具有相同权重(默认策略)
/// </summary>
Shortest = 0,
/// <summary>
/// 直线优先路径 - 优先选择主方向的路径,减少转弯次数
/// </summary>
Straightest = 1,
/// <summary>
/// 安全优先路径 - 基于障碍物距离选择安全路径,适合大型物体居中行驶
/// </summary>
SafestCenter = 2
}
/// <summary>
/// 路径点类型枚举
/// </summary>
public enum PathPointType
{
/// <summary>
/// 起点
/// </summary>
StartPoint,
/// <summary>
/// 终点
/// </summary>
EndPoint,
/// <summary>
/// 路径点
/// </summary>
WayPoint,
/// <summary>
/// 中间点
/// </summary>
IntermediatePoint
}
/// <summary>
/// 路径点数据模型
/// </summary>
[Serializable]
public class PathPoint
{
/// <summary>
/// 路径点唯一标识符
/// </summary>
public string Id { get; set; }
/// <summary>
/// 路径点3D位置不参与XML序列化
/// </summary>
[XmlIgnore]
public Point3D Position { get; set; }
/// <summary>
/// X坐标用于XML序列化
/// </summary>
[XmlElement("X")]
public double X
{
get => Position.X;
set
{
if (Position == null)
Position = new Point3D(value, 0, 0);
else
Position = new Point3D(value, Position.Y, Position.Z);
}
}
/// <summary>
/// Y坐标用于XML序列化
/// </summary>
[XmlElement("Y")]
public double Y
{
get => Position.Y;
set
{
if (Position == null)
Position = new Point3D(0, value, 0);
else
Position = new Point3D(Position.X, value, Position.Z);
}
}
/// <summary>
/// Z坐标用于XML序列化
/// </summary>
[XmlElement("Z")]
public double Z
{
get => Position.Z;
set
{
if (Position == null)
Position = new Point3D(0, 0, value);
else
Position = new Point3D(Position.X, Position.Y, value);
}
}
/// <summary>
/// 路径点名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 路径点类型
/// </summary>
public PathPointType Type { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedTime { get; set; }
/// <summary>
/// 路径点序号
/// </summary>
public int Index { get; set; }
/// <summary>
/// 备注信息
/// </summary>
public string Notes { get; set; }
/// <summary>
/// 限速(米/秒0表示未设置限速
/// </summary>
public double SpeedLimit { get; set; }
/// <summary>
/// 自定义转向半径(模型单位),用于局部急转弯场景
/// 为 null 时使用全局默认值
/// </summary>
public double? CustomTurnRadius { get; set; }
/// <summary>
/// 仅用于视图显示的点直径(模型单位)。
/// 为 null 或非正数时使用默认点类型尺寸。
/// </summary>
[XmlIgnore]
public double? VisualizationDiameter { get; set; }
/// <summary>
/// 方向类型(仅吊装路径使用)
/// 用于标识吊装路径点的移动方向垂直、纵向X轴、横向Y轴
/// </summary>
public HoistingPointDirection Direction { get; set; } = HoistingPointDirection.Vertical;
/// <summary>
/// 构造函数
/// </summary>
public PathPoint()
{
Id = Guid.NewGuid().ToString();
Position = new Point3D();
Name = string.Empty;
Type = PathPointType.WayPoint;
CreatedTime = DateTime.Now;
Index = 0;
Notes = string.Empty;
SpeedLimit = 0;
CustomTurnRadius = null;
VisualizationDiameter = null;
Direction = HoistingPointDirection.Vertical;
}
/// <summary>
/// 带参数构造函数
/// </summary>
/// <param name="position">3D坐标</param>
/// <param name="name">名称</param>
/// <param name="type">类型</param>
public PathPoint(Point3D position, string name, PathPointType type)
{
Id = Guid.NewGuid().ToString();
Position = position;
Name = name;
Type = type;
CreatedTime = DateTime.Now;
Index = 0;
Notes = string.Empty;
SpeedLimit = 0;
CustomTurnRadius = null;
VisualizationDiameter = null;
Direction = HoistingPointDirection.Vertical;
}
/// <summary>
/// 解析最终用于渲染的点半径(模型单位)。
/// </summary>
/// <param name="defaultRadius">默认点半径(模型单位)</param>
/// <returns>最终半径(模型单位)</returns>
public double ResolveVisualizationRadius(double defaultRadius)
{
if (!VisualizationDiameter.HasValue || VisualizationDiameter.Value <= 0)
{
return defaultRadius;
}
return VisualizationDiameter.Value * 0.5;
}
/// <summary>
/// 返回路径点描述字符串
/// </summary>
/// <returns></returns>
public override string ToString()
{
return $"{Name} ({Type}) - X:{Position.X:F2}, Y:{Position.Y:F2}, Z:{Position.Z:F2}";
}
}
/// <summary>
/// 路径段类型
/// </summary>
public enum PathSegmentType
{
/// <summary>
/// 直线段
/// </summary>
Straight,
/// <summary>
/// 圆弧段
/// </summary>
Arc
}
/// <summary>
/// 圆弧轨迹数据
/// </summary>
[Serializable]
public class ArcTrajectory
{
/// <summary>
/// 进入切点
/// </summary>
public Point3D Ts { get; set; }
/// <summary>
/// 退出切点
/// </summary>
public Point3D Te { get; set; }
/// <summary>
/// 圆心位置
/// </summary>
public Point3D ArcCenter { get; set; }
/// <summary>
/// 请求半径(配置的转向半径)
/// </summary>
public double RequestedRadius { get; set; }
/// <summary>
/// 实际半径(安全截断后)
/// </summary>
public double ActualRadius { get; set; }
/// <summary>
/// 偏转角(弧度)
/// </summary>
public double DeflectionAngle { get; set; }
/// <summary>
/// 圆弧长度(米)
/// </summary>
public double ArcLength { get; set; }
}
/// <summary>
/// 路径边 - 连接两个连续控制点的物理路径段
/// </summary>
[Serializable]
public class PathEdge
{
/// <summary>
/// 边唯一标识符
/// </summary>
public string Id { get; set; }
/// <summary>
/// 起始控制点ID
/// </summary>
public string StartPointId { get; set; }
/// <summary>
/// 结束控制点ID
/// </summary>
public string EndPointId { get; set; }
/// <summary>
/// 路径段类型
/// </summary>
public PathSegmentType SegmentType { get; set; }
/// <summary>
/// 圆弧轨迹数据(仅当 SegmentType == Arc 时有效)
/// </summary>
public ArcTrajectory Trajectory { get; set; }
/// <summary>
/// 边的物理长度(米)
/// 直线段:两点间距离;圆弧段:直线段长度 + 圆弧长度
/// </summary>
public double PhysicalLength { get; set; }
/// <summary>
/// 采样点序列(用于碰撞检测和动画)
/// </summary>
[XmlIgnore]
public List<Point3D> SampledPoints { get; set; }
public PathEdge()
{
Id = Guid.NewGuid().ToString();
SampledPoints = new List<Point3D>();
}
}
/// <summary>
/// 路径路线数据模型
/// </summary>
[Serializable]
public class PathRoute
{
public static bool IsTopReferenceFaceForMountMode(RailMountMode railMountMode)
{
return railMountMode == RailMountMode.UnderRail;
}
/// <summary>
/// 路径点集合(控制点)
/// </summary>
public List<PathPoint> Points { get; set; }
/// <summary>
/// 路径边集合 - 存储物理路径段
/// </summary>
public List<PathEdge> Edges { get; set; }
/// <summary>
/// 路径名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 路径ID
/// </summary>
public string Id { get; set; }
/// <summary>
/// 预估时间(秒)
/// </summary>
public double EstimatedTime { get; set; }
/// <summary>
/// 关联通道模型ID集合
/// </summary>
public List<string> AssociatedChannelIds { get; set; }
/// <summary>
/// 路径总长度(米)- 只读计算属性
///
/// 【重要】此属性实时从几何数据计算,不从数据库存储或加载。
/// 数据库中的 TotalLength 字段已废弃,保留仅用于兼容旧版本。
///
/// 计算方式:
/// - 地面路径(Ground):从 Edges.PhysicalLength 累加(支持圆弧)
/// - 空轨路径(Rail):从 Points 计算直线距离
/// - 吊装路径(Hoisting):从 Points 计算直线距离
/// </summary>
public double TotalLength
{
get
{
if (Points.Count < 2) return 0.0;
// 地面路径:优先使用 Edges支持圆弧否则从 Points 计算
if (PathType == PathType.Ground)
{
if (Edges.Count > 0)
{
double lengthInModelUnits = Edges.Sum(e => e.PhysicalLength);
return NavisworksTransport.Utils.UnitsConverter.ConvertToMeters(lengthInModelUnits);
}
return CalculateLengthFromPoints();
}
// 空轨/吊装路径:使用直线距离
if (PathType == PathType.Rail || PathType == PathType.Hoisting)
{
return CalculateLengthFromPoints();
}
// 未知类型:默认直线计算
return CalculateLengthFromPoints();
}
}
/// <summary>
/// 从路径点计算直线距离总长度(辅助方法)
/// </summary>
private double CalculateLengthFromPoints()
{
double totalLength = 0.0;
var sortedPoints = GetSortedPoints();
for (int i = 0; i < sortedPoints.Count - 1; i++)
{
var p1 = sortedPoints[i].Position;
var p2 = sortedPoints[i + 1].Position;
double dx = p2.X - p1.X;
double dy = p2.Y - p1.Y;
double dz = p2.Z - p1.Z;
double distanceInModelUnits = Math.Sqrt(dx*dx + dy*dy + dz*dz);
totalLength += NavisworksTransport.Utils.UnitsConverter.ConvertToMeters(distanceInModelUnits);
}
return totalLength;
}
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedTime { get; set; }
/// <summary>
/// 最后修改时间
/// </summary>
public DateTime LastModified { get; set; }
/// <summary>
/// 路径描述
/// </summary>
public string Description { get; set; }
/// <summary>
/// 路径是否完整到达终点
/// </summary>
public bool IsComplete { get; set; }
/// <summary>
/// 原始终点坐标(用于显示灰色未到达点)
/// </summary>
public Point3D OriginalEndPoint { get; set; }
/// <summary>
/// 实际到达点坐标
/// </summary>
public Point3D ActualEndPoint { get; set; }
/// <summary>
/// 完成百分比0-100
/// </summary>
public double CompletionPercentage { get; set; }
/// <summary>
/// 关联的网格地图(用于网格可视化)
/// </summary>
[XmlIgnore]
public NavisworksTransport.PathPlanning.GridMap AssociatedGridMap { get; set; }
/// <summary>
/// 网格大小(米)
/// </summary>
public double GridSize { get; set; }
/// <summary>
/// 最大物体长度(米) - 路径适用的物体长度限制
/// </summary>
public double MaxObjectLength { get; set; }
/// <summary>
/// 最大物体宽度(米) - 路径适用的物体宽度限制
/// </summary>
public double MaxObjectWidth { get; set; }
/// <summary>
/// 最大物体高度(米) - 路径适用的物体高度限制
/// </summary>
public double MaxObjectHeight { get; set; }
/// <summary>
/// 安全间隙(米)
/// </summary>
public double SafetyMargin { get; set; }
/// <summary>
/// 路径转弯半径(模型单位)- 路径允许的最大转弯半径
/// 物体的最小转弯半径必须小于等于此值
/// </summary>
public double TurnRadius { get; set; }
/// <summary>
/// 是否已曲线化
/// </summary>
public bool IsCurved { get; set; }
/// <summary>
/// 路径类型
/// </summary>
public PathType PathType { get; set; } = PathType.Ground;
/// <summary>
/// 吊装的提升高度(模型单位,仅当 PathType == Hoisting 时有效)
/// </summary>
public double LiftHeight { get; set; }
/// <summary>
/// Rail 路径安装方式(仅当 PathType == Rail 时有效)
/// </summary>
public RailMountMode RailMountMode { get; set; }
/// <summary>
/// Rail 路径参考线定义方式(仅当 PathType == Rail 时有效)
/// </summary>
public RailPathDefinitionMode RailPathDefinitionMode { get; set; }
/// <summary>
/// Rail 路径优先法向(宿主坐标)。
/// 仅在存在显式安装面/业务法向时使用;为空时退回当前默认求解。
/// </summary>
public Point3D RailPreferredNormal { get; set; }
/// <summary>
/// Rail 安装法向偏移(模型单位)。
/// 沿 Rail 最终法向对安装参考点施加额外偏移,用于快速微调路径。
/// </summary>
public double RailNormalOffset { get; set; }
// 数据库分析相关属性
/// <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>
public PathRoute()
{
Points = new List<PathPoint>();
Edges = new List<PathEdge>();
Name = string.Empty;
Id = Guid.NewGuid().ToString();
EstimatedTime = 0.0;
AssociatedChannelIds = new List<string>();
// TotalLength计算属性自动从几何数据计算无需初始化
CreatedTime = DateTime.Now;
LastModified = DateTime.Now;
Description = string.Empty;
TurnRadius = 0.0; // 0.0 表示未设置,实际使用时从配置获取默认值
IsCurved = false;
RailMountMode = RailMountMode.UnderRail;
RailPathDefinitionMode = RailPathDefinitionMode.LegacySuspensionPoint;
RailPreferredNormal = null;
RailNormalOffset = 0.0;
}
/// <summary>
/// 带参数构造函数
/// </summary>
/// <param name="name">路径名称</param>
public PathRoute(string name)
{
Points = new List<PathPoint>();
Edges = new List<PathEdge>();
Name = name;
Id = Guid.NewGuid().ToString();
EstimatedTime = 0.0;
AssociatedChannelIds = new List<string>();
// TotalLength计算属性自动从几何数据计算无需初始化
CreatedTime = DateTime.Now;
TurnRadius = 0.0; // 0.0 表示未设置,实际使用时从配置获取默认值
IsCurved = false;
LastModified = DateTime.Now;
Description = string.Empty;
RailMountMode = RailMountMode.UnderRail;
RailPathDefinitionMode = RailPathDefinitionMode.LegacySuspensionPoint;
RailPreferredNormal = null;
RailNormalOffset = 0.0;
}
/// <summary>
/// 添加路径点
/// </summary>
/// <param name="point">路径点</param>
public void AddPoint(PathPoint point)
{
Points.Add(point);
// 调用统一更新函数(内部会重新设置索引)
RecalculateAndSaveRoute("添加路径点");
}
/// <summary>
/// 在指定位置插入路径点
/// </summary>
/// <param name="point">要插入的路径点</param>
/// <param name="insertIndex">插入位置索引</param>
public void InsertPoint(PathPoint point, int insertIndex)
{
Points.Insert(insertIndex, point);
// 调用统一更新函数(内部会重新设置索引)
RecalculateAndSaveRoute("插入路径点");
}
/// <summary>
/// 移除路径点
/// </summary>
/// <param name="point">路径点</param>
public bool RemovePoint(PathPoint point)
{
bool removed = Points.Remove(point);
if (removed)
{
// 调用统一更新函数(内部会重新设置索引)
RecalculateAndSaveRoute("移除路径点");
}
return removed;
}
/// <summary>
/// 根据ID移除路径点
/// </summary>
/// <param name="pointId">路径点ID</param>
/// <returns>是否成功移除</returns>
public bool RemovePoint(string pointId)
{
var point = Points.FirstOrDefault(p => p.Id == pointId);
if (point != null)
{
return RemovePoint(point);
}
return false;
}
/// <summary>
/// 清空所有路径点
/// </summary>
public void ClearPoints()
{
Points.Clear();
Edges.Clear();
IsCurved = false;
// 调用统一更新函数
RecalculateAndSaveRoute("清空路径点");
}
/// <summary>
/// 获取起点
/// </summary>
/// <returns></returns>
public PathPoint GetStartPoint()
{
return Points.Find(p => p.Type == PathPointType.StartPoint);
}
/// <summary>
/// 获取终点
/// </summary>
/// <returns></returns>
public PathPoint GetEndPoint()
{
return Points.Find(p => p.Type == PathPointType.EndPoint);
}
/// <summary>
/// 获取所有路径点(按序号排序)
/// </summary>
/// <returns></returns>
public List<PathPoint> GetSortedPoints()
{
var sortedPoints = new List<PathPoint>(Points);
sortedPoints.Sort((p1, p2) => p1.Index.CompareTo(p2.Index));
return sortedPoints;
}
/// <summary>
/// 重新计算路径数据(不保存到数据库)
/// 用于路径优化等临时计算场景
/// </summary>
/// <param name="context">上下文信息(用于日志)</param>
public void RecalculateRoute(string context = "路径数据更新")
{
if (Points.Count < 2)
{
LogManager.Debug($"跳过路径数据计算(路径点不足): {Name}, 原因: {context}");
return;
}
try
{
// 1. 重新设置路径点索引
for (int i = 0; i < Points.Count; i++)
{
Points[i].Index = i;
}
// 2. 根据路径类型决定是否重新计算曲线
if (PathType == PathType.Ground)
{
// 地面路径:进行曲线化
double samplingStep = ConfigManager.Instance.Current.PathEditing.ArcSamplingStep;
TurnRadius = ConfigManager.Instance.Current.PathEditing.DefaultPathTurnRadius;
List<string> warnings;
PathCurveEngine.ApplyCurvatureToRoute(this, samplingStep, out warnings);
LogManager.Debug($"地面路径已曲线化: {Name}, 原因: {context}, 转弯半径: {TurnRadius:F2}, 边数: {Edges.Count}");
// 如果有警告,记录日志
if (warnings != null && warnings.Count > 0)
{
foreach (var warning in warnings)
{
LogManager.Warning($"路径曲线化警告: {warning}");
}
}
}
else if (PathType == PathType.Rail || PathType == PathType.Hoisting)
{
// 空轨/吊装路径:使用直线,不需要曲线化
Edges.Clear();
IsCurved = false;
string pathTypeName = PathType == PathType.Rail ? "空轨" : "吊装";
LogManager.Debug($"{pathTypeName}路径已更新(直线模式): {Name}, 原因: {context}");
}
else
{
// 未知路径类型:使用直线
Edges.Clear();
IsCurved = false;
LogManager.Debug($"未知路径类型,使用直线模式: {Name}, 原因: {context}");
}
// 3. 更新最后修改时间
LastModified = DateTime.Now;
LogManager.Debug($"路径数据已计算: {Name}, 原因: {context}, 总长度: {TotalLength:F2}米");
}
catch (Exception ex)
{
LogManager.Error($"路径数据计算失败({context}: {ex.Message}", ex);
}
}
/// <summary>
/// 重新计算路径数据并保存到数据库
/// 统一的路径数据更新入口:重新设置索引、更新长度、计算曲线、保存到数据库
/// </summary>
/// <param name="context">上下文信息(用于日志)</param>
public void RecalculateAndSaveRoute(string context = "路径数据更新")
{
// 先执行计算
RecalculateRoute(context);
// 然后保存到数据库
try
{
if (Points.Count >= 2)
{
var pathPlanningManager = PathPlanningManager.Instance;
if (pathPlanningManager != null)
{
pathPlanningManager.SavePathToDatabase(this);
LogManager.Info($"路径数据已保存到数据库: {Name}, 原因: {context}, 总长度: {TotalLength:F2}米");
}
}
}
catch (Exception ex)
{
LogManager.Error($"路径数据保存失败({context}: {ex.Message}", ex);
}
}
/// <summary>
/// 验证路径有效性
/// </summary>
/// <returns></returns>
public bool IsValid()
{
// 至少需要起点和终点
if (Points.Count < 2) return false;
// 必须有且仅有一个起点和一个终点
var startPoints = Points.FindAll(p => p.Type == PathPointType.StartPoint);
var endPoints = Points.FindAll(p => p.Type == PathPointType.EndPoint);
return startPoints.Count == 1 && endPoints.Count == 1;
}
/// <summary>
/// 克隆路径
/// </summary>
/// <returns>克隆的路径对象</returns>
public PathRoute Clone()
{
var clonedRoute = new PathRoute(Name + "_Copy")
{
Id = Guid.NewGuid().ToString(),
EstimatedTime = EstimatedTime,
// TotalLength计算属性自动从几何数据计算克隆时会重新计算
CreatedTime = DateTime.Now,
LastModified = DateTime.Now,
Description = Description,
AssociatedChannelIds = new List<string>(AssociatedChannelIds),
IsComplete = IsComplete,
OriginalEndPoint = OriginalEndPoint,
ActualEndPoint = ActualEndPoint,
CompletionPercentage = CompletionPercentage,
AssociatedGridMap = AssociatedGridMap, // 共享引用,不深拷贝
GridSize = GridSize,
MaxObjectLength = MaxObjectLength,
MaxObjectWidth = MaxObjectWidth,
MaxObjectHeight = MaxObjectHeight,
SafetyMargin = SafetyMargin,
TurnRadius = TurnRadius,
IsCurved = IsCurved,
PathType = PathType,
LiftHeight = LiftHeight,
RailMountMode = RailMountMode,
RailPathDefinitionMode = RailPathDefinitionMode,
RailNormalOffset = RailNormalOffset,
RailPreferredNormal = RailPreferredNormal
};
var pointIdMap = new Dictionary<string, string>();
// 克隆所有路径点
foreach (var point in Points)
{
var clonedPoint = new PathPoint(point.Position, point.Name, point.Type)
{
Id = Guid.NewGuid().ToString(),
Index = point.Index,
CreatedTime = point.CreatedTime,
Notes = point.Notes,
SpeedLimit = point.SpeedLimit,
CustomTurnRadius = point.CustomTurnRadius,
VisualizationDiameter = point.VisualizationDiameter,
Direction = point.Direction
};
clonedRoute.Points.Add(clonedPoint);
pointIdMap[point.Id] = clonedPoint.Id;
}
foreach (var edge in Edges)
{
var clonedEdge = new PathEdge
{
Id = Guid.NewGuid().ToString(),
StartPointId = pointIdMap.ContainsKey(edge.StartPointId) ? pointIdMap[edge.StartPointId] : edge.StartPointId,
EndPointId = pointIdMap.ContainsKey(edge.EndPointId) ? pointIdMap[edge.EndPointId] : edge.EndPointId,
SegmentType = edge.SegmentType,
PhysicalLength = edge.PhysicalLength,
Trajectory = edge.Trajectory != null
? new ArcTrajectory
{
Ts = edge.Trajectory.Ts,
Te = edge.Trajectory.Te,
ArcCenter = edge.Trajectory.ArcCenter,
RequestedRadius = edge.Trajectory.RequestedRadius,
ActualRadius = edge.Trajectory.ActualRadius,
DeflectionAngle = edge.Trajectory.DeflectionAngle,
ArcLength = edge.Trajectory.ArcLength
}
: null,
SampledPoints = edge.SampledPoints != null
? new List<Point3D>(edge.SampledPoints)
: new List<Point3D>()
};
clonedRoute.Edges.Add(clonedEdge);
}
return clonedRoute;
}
/// <summary>
/// 返回路径描述字符串
/// </summary>
/// <returns></returns>
public override string ToString()
{
return $"{Name} - 点数:{Points.Count}, 长度:{TotalLength:F2}m, 预估时间:{EstimatedTime:F1}s";
}
}
/// <summary>
/// 2D地图坐标
/// </summary>
[Serializable]
public class MapPoint2D
{
/// <summary>
/// X坐标
/// </summary>
public double X { get; set; }
/// <summary>
/// Y坐标
/// </summary>
public double Y { get; set; }
/// <summary>
/// 构造函数
/// </summary>
public MapPoint2D()
{
X = 0.0;
Y = 0.0;
}
/// <summary>
/// 带参数构造函数
/// </summary>
/// <param name="x">X坐标</param>
/// <param name="y">Y坐标</param>
public MapPoint2D(double x, double y)
{
X = x;
Y = y;
}
/// <summary>
/// 返回坐标描述字符串
/// </summary>
/// <returns></returns>
public override string ToString()
{
return $"({X:F2}, {Y:F2})";
}
}
/// <summary>
/// 通道边界信息
/// </summary>
[Serializable]
public class ChannelBounds
{
/// <summary>
/// 最小坐标点
/// </summary>
public Point3D MinPoint { get; set; }
/// <summary>
/// 最大坐标点
/// </summary>
public Point3D MaxPoint { get; set; }
/// <summary>
/// 中心点
/// </summary>
public Point3D Center { get; set; }
/// <summary>
/// 通道高度Z轴方向
/// </summary>
public double Height { get; set; }
/// <summary>
/// 构造函数
/// </summary>
public ChannelBounds()
{
MinPoint = new Point3D();
MaxPoint = new Point3D();
Center = new Point3D();
Height = 0.0;
}
/// <summary>
/// 带包围盒参数构造函数
/// </summary>
/// <param name="boundingBox">Navisworks包围盒</param>
public ChannelBounds(BoundingBox3D boundingBox)
{
MinPoint = boundingBox.Min;
MaxPoint = boundingBox.Max;
Center = new Point3D(
(MinPoint.X + MaxPoint.X) / 2,
(MinPoint.Y + MaxPoint.Y) / 2,
(MinPoint.Z + MaxPoint.Z) / 2
);
Height = MaxPoint.Z - MinPoint.Z;
}
/// <summary>
/// 获取2D投影范围
/// </summary>
/// <param name="min">最小点</param>
/// <param name="max">最大点</param>
public void Get2DProjection(out MapPoint2D min, out MapPoint2D max)
{
min = new MapPoint2D(MinPoint.X, MinPoint.Y);
max = new MapPoint2D(MaxPoint.X, MaxPoint.Y);
}
}
/// <summary>
/// 通道选择结果
/// </summary>
public class ChannelSelectionResult
{
/// <summary>
/// 是否成功
/// </summary>
public bool Success { get; set; } = false;
/// <summary>
/// 结果消息
/// </summary>
public string Message { get; set; } = "";
/// <summary>
/// 总模型项数量
/// </summary>
public int TotalModelItems { get; set; } = 0;
/// <summary>
/// 自动检测的通道
/// </summary>
public ModelItemCollection AutoDetectedChannels { get; set; } = new ModelItemCollection();
/// <summary>
/// 已标记物流属性的模型项
/// </summary>
public ModelItemCollection LogisticsMarkedItems { get; set; } = new ModelItemCollection();
/// <summary>
/// 可通行区域
/// </summary>
public ModelItemCollection TraversableAreas { get; set; } = new ModelItemCollection();
/// <summary>
/// 通道类型模型项
/// </summary>
public ModelItemCollection ChannelItems { get; set; } = new ModelItemCollection();
}
/// <summary>
/// 通道选择对话框结果
/// </summary>
public class ChannelSelectionDialogResult
{
/// <summary>
/// 是否成功
/// </summary>
public bool Success { get; set; } = false;
/// <summary>
/// 结果消息
/// </summary>
public string Message { get; set; } = "";
/// <summary>
/// 选中的通道
/// </summary>
public ModelItemCollection SelectedChannels { get; set; } = new ModelItemCollection();
/// <summary>
/// 选择方法
/// </summary>
public ChannelSelectionMethod SelectionMethod { get; set; } = ChannelSelectionMethod.Manual;
/// <summary>
/// 高度限制(米)
/// </summary>
public double HeightLimit { get; set; } = 3.0;
}
/// <summary>
/// 通道选择方法枚举
/// </summary>
public enum ChannelSelectionMethod
{
/// <summary>
/// 手动选择
/// </summary>
Manual,
/// <summary>
/// 自动检测
/// </summary>
AutoDetect,
/// <summary>
/// 基于物流属性
/// </summary>
LogisticsAttribute,
/// <summary>
/// 可通行区域
/// </summary>
TraversableAreas,
/// <summary>
/// 通道类型
/// </summary>
ChannelType
}
/// <summary>
/// 通道选择对话框
/// </summary>
public class ChannelSelectionDialog : System.Windows.Forms.Form
{
private ChannelSelectionResult _analysisResult;
private ModelItemCollection _selectedChannels;
private ChannelSelectionMethod _selectionMethod;
private double _heightLimit;
private const string ChannelPreviewHighlightCategory = "channel_preview";
/// <summary>
/// 选中的通道
/// </summary>
public ModelItemCollection SelectedChannels { get { return _selectedChannels; } }
/// <summary>
/// 选择方法
/// </summary>
public ChannelSelectionMethod SelectionMethod { get { return _selectionMethod; } }
/// <summary>
/// 高度限制(米)
/// </summary>
public double HeightLimit { get { return _heightLimit; } }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="analysisResult">通道分析结果</param>
public ChannelSelectionDialog(ChannelSelectionResult analysisResult)
{
_analysisResult = analysisResult ?? new ChannelSelectionResult();
_selectedChannels = new ModelItemCollection();
_selectionMethod = ChannelSelectionMethod.Manual;
_heightLimit = 3.0;
InitializeComponent();
LoadAnalysisData();
}
// UI控件
private System.Windows.Forms.TabControl tabControl;
private System.Windows.Forms.ListView channelListView;
private System.Windows.Forms.Label statisticsLabel;
private System.Windows.Forms.NumericUpDown heightLimitNumericUpDown;
private System.Windows.Forms.Button previewButton;
private System.Windows.Forms.Button clearHighlightButton;
private System.Windows.Forms.CheckBox selectAllCheckBox;
/// <summary>
/// 初始化对话框组件
/// </summary>
private void InitializeComponent()
{
this.Text = "通道选择";
this.Size = new System.Drawing.Size(700, 500);
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
// 创建选项卡控件
tabControl = new System.Windows.Forms.TabControl
{
Location = new System.Drawing.Point(10, 10),
Size = new System.Drawing.Size(670, 350),
Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left |
System.Windows.Forms.AnchorStyles.Right | System.Windows.Forms.AnchorStyles.Bottom
};
// 创建通道列表选项卡
var channelListTab = new System.Windows.Forms.TabPage("通道列表");
CreateChannelListTab(channelListTab);
tabControl.TabPages.Add(channelListTab);
// 创建筛选选项卡
var filterTab = new System.Windows.Forms.TabPage("筛选选项");
CreateFilterTab(filterTab);
tabControl.TabPages.Add(filterTab);
this.Controls.Add(tabControl);
// 统计信息标签
statisticsLabel = new System.Windows.Forms.Label
{
Location = new System.Drawing.Point(10, 370),
Size = new System.Drawing.Size(400, 60),
Text = "正在加载通道信息...",
Font = new System.Drawing.Font("微软雅黑", 8),
Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left
};
this.Controls.Add(statisticsLabel);
// 预览按钮
previewButton = new System.Windows.Forms.Button
{
Text = "预览选中",
Size = new System.Drawing.Size(80, 25),
Location = new System.Drawing.Point(420, 370),
Font = new System.Drawing.Font("微软雅黑", 8),
Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left
};
previewButton.Click += PreviewButton_Click;
this.Controls.Add(previewButton);
// 清除高亮按钮
clearHighlightButton = new System.Windows.Forms.Button
{
Text = "清除高亮",
Size = new System.Drawing.Size(80, 25),
Location = new System.Drawing.Point(510, 370),
Font = new System.Drawing.Font("微软雅黑", 8),
Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left
};
clearHighlightButton.Click += ClearHighlightButton_Click;
this.Controls.Add(clearHighlightButton);
// 确定按钮
var okButton = new System.Windows.Forms.Button
{
Text = "确定",
Size = new System.Drawing.Size(75, 30),
Location = new System.Drawing.Point(520, 440),
Font = new System.Drawing.Font("微软雅黑", 8),
DialogResult = System.Windows.Forms.DialogResult.OK,
Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right
};
okButton.Click += OkButton_Click;
this.Controls.Add(okButton);
// 取消按钮
var cancelButton = new System.Windows.Forms.Button
{
Text = "取消",
Size = new System.Drawing.Size(75, 30),
Location = new System.Drawing.Point(605, 440),
Font = new System.Drawing.Font("微软雅黑", 8),
DialogResult = System.Windows.Forms.DialogResult.Cancel,
Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right
};
this.Controls.Add(cancelButton);
}
/// <summary>
/// 创建通道列表选项卡
/// </summary>
private void CreateChannelListTab(System.Windows.Forms.TabPage tabPage)
{
// 全选复选框
selectAllCheckBox = new System.Windows.Forms.CheckBox
{
Text = "全选",
Location = new System.Drawing.Point(10, 10),
Size = new System.Drawing.Size(60, 20),
Font = new System.Drawing.Font("微软雅黑", 8)
};
selectAllCheckBox.CheckedChanged += SelectAllCheckBox_CheckedChanged;
tabPage.Controls.Add(selectAllCheckBox);
// 通道列表
channelListView = new System.Windows.Forms.ListView
{
Location = new System.Drawing.Point(10, 35),
Size = new System.Drawing.Size(640, 280),
View = System.Windows.Forms.View.Details,
FullRowSelect = true,
GridLines = true,
CheckBoxes = true,
Font = new System.Drawing.Font("微软雅黑", 8),
Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left |
System.Windows.Forms.AnchorStyles.Right | System.Windows.Forms.AnchorStyles.Bottom
};
// 添加列
channelListView.Columns.Add("名称", 200);
channelListView.Columns.Add("类型", 80);
channelListView.Columns.Add("可通行", 60);
channelListView.Columns.Add("物体尺寸", 80);
channelListView.Columns.Add("长度(m)", 80);
channelListView.Columns.Add("宽度(m)", 80);
channelListView.Columns.Add("高度(m)", 80);
channelListView.ItemChecked += ChannelListView_ItemChecked;
tabPage.Controls.Add(channelListView);
}
/// <summary>
/// 创建筛选选项卡
/// </summary>
private void CreateFilterTab(System.Windows.Forms.TabPage tabPage)
{
// 高度限制选择
var heightLimitLabel = new System.Windows.Forms.Label
{
Text = "高度限制(m):",
Location = new System.Drawing.Point(10, 20),
Size = new System.Drawing.Size(80, 20),
Font = new System.Drawing.Font("微软雅黑", 8)
};
tabPage.Controls.Add(heightLimitLabel);
heightLimitNumericUpDown = new System.Windows.Forms.NumericUpDown
{
Location = new System.Drawing.Point(100, 18),
Size = new System.Drawing.Size(120, 25),
Font = new System.Drawing.Font("微软雅黑", 8),
Minimum = 1.0m,
Maximum = 20.0m,
DecimalPlaces = 1,
Value = 3.0m
};
heightLimitNumericUpDown.ValueChanged += HeightLimitNumericUpDown_ValueChanged;
tabPage.Controls.Add(heightLimitNumericUpDown);
// 筛选方式选择
var filterMethodLabel = new System.Windows.Forms.Label
{
Text = "筛选方式:",
Location = new System.Drawing.Point(10, 60),
Size = new System.Drawing.Size(200, 20),
Font = new System.Drawing.Font("微软雅黑", 8)
};
tabPage.Controls.Add(filterMethodLabel);
var filterOptions = new System.Windows.Forms.RadioButton[]
{
new System.Windows.Forms.RadioButton { Text = "显示所有检测到的通道", Location = new System.Drawing.Point(20, 85), Size = new System.Drawing.Size(200, 20), Checked = true },
new System.Windows.Forms.RadioButton { Text = "仅显示已标记物流属性的通道", Location = new System.Drawing.Point(20, 110), Size = new System.Drawing.Size(200, 20) },
new System.Windows.Forms.RadioButton { Text = "仅显示可通行区域", Location = new System.Drawing.Point(20, 135), Size = new System.Drawing.Size(200, 20) },
new System.Windows.Forms.RadioButton { Text = "自动检测通道", Location = new System.Drawing.Point(20, 160), Size = new System.Drawing.Size(200, 20) }
};
foreach (var option in filterOptions)
{
option.Font = new System.Drawing.Font("微软雅黑", 8);
option.CheckedChanged += FilterOption_CheckedChanged;
tabPage.Controls.Add(option);
}
// 应用筛选按钮
var applyFilterButton = new System.Windows.Forms.Button
{
Text = "应用筛选",
Location = new System.Drawing.Point(20, 200),
Size = new System.Drawing.Size(100, 30),
Font = new System.Drawing.Font("微软雅黑", 8)
};
applyFilterButton.Click += ApplyFilterButton_Click;
tabPage.Controls.Add(applyFilterButton);
}
/// <summary>
/// 加载分析数据
/// </summary>
private void LoadAnalysisData()
{
try
{
if (channelListView == null) return;
channelListView.Items.Clear();
// 根据分析结果填充通道列表
var channelsToShow = _analysisResult.ChannelItems.Count > 0
? _analysisResult.ChannelItems
: _analysisResult.AutoDetectedChannels;
foreach (ModelItem channel in channelsToShow)
{
var item = new System.Windows.Forms.ListViewItem(channel.DisplayName ?? "未命名通道");
item.Tag = channel;
// 获取通道属性信息
string channelType = "通道";
string traversable = "是";
string objectSize = "标准";
try
{
if (CategoryAttributeManager.HasLogisticsAttributes(channel))
{
// 这里可以添加更详细的属性读取逻辑
}
}
catch (Exception ex)
{
LogManager.Debug($"[通道列表] 获取物流属性失败: {ex.Message}");
}
// 获取尺寸信息
double length = 0, width = 0, height = 0;
try
{
var boundingBox = channel.BoundingBox();
if (boundingBox != null)
{
length = Math.Abs(boundingBox.Max.X - boundingBox.Min.X);
width = Math.Abs(boundingBox.Max.Y - boundingBox.Min.Y);
height = Math.Abs(boundingBox.Max.Z - boundingBox.Min.Z);
}
}
catch (Exception ex)
{
LogManager.Debug($"[通道列表] 获取包围盒失败: {ex.Message}");
}
// 添加子项
item.SubItems.Add(channelType);
item.SubItems.Add(traversable);
item.SubItems.Add(objectSize);
item.SubItems.Add(length.ToString("F2"));
item.SubItems.Add(width.ToString("F2"));
item.SubItems.Add(height.ToString("F2"));
channelListView.Items.Add(item);
}
// 更新统计信息
UpdateStatistics();
// 默认选择所有通道
if (_analysisResult.Success && channelsToShow.Count > 0)
{
_selectedChannels = new ModelItemCollection();
foreach (ModelItem channel in channelsToShow)
{
_selectedChannels.Add(channel);
}
}
}
catch (Exception ex)
{
if (statisticsLabel != null)
{
statisticsLabel.Text = $"加载通道数据失败: {ex.Message}";
}
}
}
/// <summary>
/// 更新统计信息
/// </summary>
private void UpdateStatistics()
{
if (statisticsLabel == null) return;
var totalChannels = channelListView.Items.Count;
var selectedChannels = channelListView.CheckedItems.Count;
var logisticsMarked = _analysisResult.LogisticsMarkedItems.Count;
var autoDetected = _analysisResult.AutoDetectedChannels.Count;
statisticsLabel.Text = $"统计信息:\n" +
$"总通道数: {totalChannels} 已选择: {selectedChannels}\n" +
$"已标记物流属性: {logisticsMarked} 自动检测: {autoDetected}";
}
#region
/// <summary>
/// 预览按钮点击事件
/// </summary>
private void PreviewButton_Click(object sender, EventArgs e)
{
try
{
var selectedItems = new ModelItemCollection();
foreach (System.Windows.Forms.ListViewItem item in channelListView.CheckedItems)
{
if (item.Tag is ModelItem channel)
{
selectedItems.Add(channel);
}
}
if (selectedItems.Count > 0)
{
// 高亮显示选中的通道
ModelHighlightHelper.HighlightItems(ChannelPreviewHighlightCategory, selectedItems);
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show($"预览失败: {ex.Message}", "错误",
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
}
}
/// <summary>
/// 清除高亮按钮点击事件
/// </summary>
private void ClearHighlightButton_Click(object sender, EventArgs e)
{
try
{
ModelHighlightHelper.ClearCategory(ChannelPreviewHighlightCategory);
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show($"清除高亮失败: {ex.Message}", "错误",
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
}
}
/// <summary>
/// 确定按钮点击事件
/// </summary>
private void OkButton_Click(object sender, EventArgs e)
{
try
{
_selectedChannels.Clear();
foreach (System.Windows.Forms.ListViewItem item in channelListView.CheckedItems)
{
if (item.Tag is ModelItem channel)
{
_selectedChannels.Add(channel);
}
}
_heightLimit = (double)(heightLimitNumericUpDown?.Value ?? 3.0m);
_selectionMethod = ChannelSelectionMethod.Manual; // 可以根据选项卡或筛选方式设置
// 清除高亮
try
{
ModelHighlightHelper.ClearCategory(ChannelPreviewHighlightCategory);
}
catch (Exception ex)
{
LogManager.Debug($"[通道预览] 清除高亮失败: {ex.Message}");
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show($"确认选择失败: {ex.Message}", "错误",
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
}
}
/// <summary>
/// 全选复选框状态改变事件
/// </summary>
private void SelectAllCheckBox_CheckedChanged(object sender, EventArgs e)
{
try
{
bool isChecked = selectAllCheckBox.Checked;
foreach (System.Windows.Forms.ListViewItem item in channelListView.Items)
{
item.Checked = isChecked;
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show($"全选操作失败: {ex.Message}", "错误",
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
}
}
/// <summary>
/// 通道列表项选中状态改变事件
/// </summary>
private void ChannelListView_ItemChecked(object sender, System.Windows.Forms.ItemCheckedEventArgs e)
{
UpdateStatistics();
}
/// <summary>
/// 高度限制数值改变事件
/// </summary>
private void HeightLimitNumericUpDown_ValueChanged(object sender, EventArgs e)
{
_heightLimit = (double)heightLimitNumericUpDown.Value;
}
/// <summary>
/// 筛选选项改变事件
/// </summary>
private void FilterOption_CheckedChanged(object sender, EventArgs e)
{
// 可以根据需要实现筛选逻辑
}
/// <summary>
/// 应用筛选按钮点击事件
/// </summary>
private void ApplyFilterButton_Click(object sender, EventArgs e)
{
try
{
// 重新加载数据应用筛选
LoadAnalysisData();
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show($"应用筛选失败: {ex.Message}", "错误",
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
}
}
#endregion
}
#region
/// <summary>
/// 路径验证结果
/// </summary>
public class PathValidationResult
{
/// <summary>
/// 路径ID
/// </summary>
public string RouteId { get; set; } = "";
/// <summary>
/// 路径名称
/// </summary>
public string RouteName { get; set; } = "";
/// <summary>
/// 验证是否通过
/// </summary>
public bool IsValid { get; set; } = false;
/// <summary>
/// 验证消息
/// </summary>
public string Message { get; set; } = "";
/// <summary>
/// 错误列表
/// </summary>
public List<string> Errors { get; set; } = new List<string>();
/// <summary>
/// 警告列表
/// </summary>
public List<string> Warnings { get; set; } = new List<string>();
/// <summary>
/// 验证时间
/// </summary>
public DateTime ValidationTime { get; set; } = DateTime.Now;
/// <summary>
/// 添加错误信息
/// </summary>
/// <param name="error">错误信息</param>
public void AddError(string error)
{
if (!string.IsNullOrEmpty(error) && !Errors.Contains(error))
{
Errors.Add(error);
}
}
/// <summary>
/// 添加警告信息
/// </summary>
/// <param name="warning">警告信息</param>
public void AddWarning(string warning)
{
if (!string.IsNullOrEmpty(warning) && !Warnings.Contains(warning))
{
Warnings.Add(warning);
}
}
/// <summary>
/// 获取详细验证报告
/// </summary>
/// <returns>验证报告文本</returns>
public string GetDetailedReport()
{
var report = new StringBuilder();
report.AppendLine($"路径验证报告 - {RouteName} ({RouteId})");
report.AppendLine($"验证时间: {ValidationTime:yyyy-MM-dd HH:mm:ss}");
report.AppendLine($"验证结果: {(IsValid ? "" : "")}");
report.AppendLine($"消息: {Message}");
if (Errors.Count > 0)
{
report.AppendLine("\n错误列表:");
for (int i = 0; i < Errors.Count; i++)
{
report.AppendLine($" {i + 1}. {Errors[i]}");
}
}
if (Warnings.Count > 0)
{
report.AppendLine("\n警告列表:");
for (int i = 0; i < Warnings.Count; i++)
{
report.AppendLine($" {i + 1}. {Warnings[i]}");
}
}
return report.ToString();
}
}
/// <summary>
/// 路径优化选项
/// </summary>
public class PathOptimizationOptions
{
/// <summary>
/// 移除重复点
/// </summary>
public bool RemoveDuplicatePoints { get; set; } = true;
/// <summary>
/// 路径平滑化
/// </summary>
public bool SmoothPath { get; set; } = true;
/// <summary>
/// 优化路径角度
/// </summary>
public bool OptimizeAngles { get; set; } = true;
/// <summary>
/// 调整到通道中心
/// </summary>
public bool AdjustToChannels { get; set; } = false;
/// <summary>
/// 重复点检测阈值(米)
/// </summary>
public double DuplicatePointThreshold { get; set; } = 0.01;
/// <summary>
/// 角度优化容差(弧度)
/// </summary>
public double AngleOptimizationTolerance { get; set; } = 0.1;
/// <summary>
/// 平滑强度0-1
/// </summary>
public double SmoothingStrength { get; set; } = 0.5;
/// <summary>
/// 创建默认优化选项
/// </summary>
/// <returns>默认优化选项</returns>
public static PathOptimizationOptions CreateDefault()
{
return new PathOptimizationOptions
{
RemoveDuplicatePoints = true,
SmoothPath = true,
OptimizeAngles = true,
AdjustToChannels = false,
DuplicatePointThreshold = 0.01,
AngleOptimizationTolerance = 0.1,
SmoothingStrength = 0.5
};
}
/// <summary>
/// 创建保守优化选项(仅基础优化)
/// </summary>
/// <returns>保守优化选项</returns>
public static PathOptimizationOptions CreateConservative()
{
return new PathOptimizationOptions
{
RemoveDuplicatePoints = true,
SmoothPath = false,
OptimizeAngles = false,
AdjustToChannels = false,
DuplicatePointThreshold = 0.001
};
}
/// <summary>
/// 创建激进优化选项(所有优化)
/// </summary>
/// <returns>激进优化选项</returns>
public static PathOptimizationOptions CreateAggressive()
{
return new PathOptimizationOptions
{
RemoveDuplicatePoints = true,
SmoothPath = true,
OptimizeAngles = true,
AdjustToChannels = true,
DuplicatePointThreshold = 0.05,
AngleOptimizationTolerance = 0.2,
SmoothingStrength = 0.8
};
}
}
/// <summary>
/// 路径优化结果
/// </summary>
public class PathOptimizationResult
{
/// <summary>
/// 优化是否成功
/// </summary>
public bool Success { get; set; } = false;
/// <summary>
/// 优化消息
/// </summary>
public string Message { get; set; } = "";
/// <summary>
/// 原始路径
/// </summary>
public PathRoute OriginalRoute { get; set; }
/// <summary>
/// 优化后的路径
/// </summary>
public PathRoute OptimizedRoute { get; set; }
/// <summary>
/// 原始路径长度
/// </summary>
public double OriginalLength { get; set; } = 0.0;
/// <summary>
/// 优化后路径长度
/// </summary>
public double OptimizedLength { get; set; } = 0.0;
/// <summary>
/// 长度减少量
/// </summary>
public double LengthReduction { get; set; } = 0.0;
/// <summary>
/// 长度减少百分比
/// </summary>
public double LengthReductionPercentage => OriginalLength > 0 ? (LengthReduction / OriginalLength) * 100 : 0;
/// <summary>
/// 原始点数
/// </summary>
public int OriginalPointCount { get; set; } = 0;
/// <summary>
/// 优化后点数
/// </summary>
public int OptimizedPointCount { get; set; } = 0;
/// <summary>
/// 点数减少量
/// </summary>
public int PointReduction { get; set; } = 0;
/// <summary>
/// 点数减少百分比
/// </summary>
public double PointReductionPercentage => OriginalPointCount > 0 ? ((double)PointReduction / OriginalPointCount) * 100 : 0;
/// <summary>
/// 优化步骤记录
/// </summary>
public List<string> OptimizationSteps { get; set; } = new List<string>();
/// <summary>
/// 优化时间
/// </summary>
public DateTime OptimizationTime { get; set; } = DateTime.Now;
/// <summary>
/// 优化耗时(毫秒)
/// </summary>
public long ElapsedMilliseconds { get; set; } = 0;
/// <summary>
/// 添加优化步骤记录
/// </summary>
/// <param name="step">优化步骤描述</param>
public void AddOptimizationStep(string step)
{
if (!string.IsNullOrEmpty(step))
{
OptimizationSteps.Add($"[{DateTime.Now:HH:mm:ss}] {step}");
}
}
/// <summary>
/// 获取详细优化报告
/// </summary>
/// <returns>优化报告文本</returns>
public string GetDetailedReport()
{
var report = new StringBuilder();
report.AppendLine($"路径优化报告");
report.AppendLine($"优化时间: {OptimizationTime:yyyy-MM-dd HH:mm:ss}");
report.AppendLine($"耗时: {ElapsedMilliseconds} 毫秒");
report.AppendLine($"优化结果: {(Success ? "" : "")}");
report.AppendLine($"消息: {Message}");
if (Success)
{
report.AppendLine($"\n原始路径:");
report.AppendLine($" 长度: {OriginalLength:F3} 米");
report.AppendLine($" 点数: {OriginalPointCount}");
report.AppendLine($"\n优化后路径:");
report.AppendLine($" 长度: {OptimizedLength:F3} 米");
report.AppendLine($" 点数: {OptimizedPointCount}");
report.AppendLine($"\n优化效果:");
report.AppendLine($" 长度减少: {LengthReduction:F3} 米 ({LengthReductionPercentage:F1}%)");
report.AppendLine($" 点数减少: {PointReduction} 个 ({PointReductionPercentage:F1}%)");
}
if (OptimizationSteps.Count > 0)
{
report.AppendLine("\n优化步骤:");
for (int i = 0; i < OptimizationSteps.Count; i++)
{
report.AppendLine($" {i + 1}. {OptimizationSteps[i]}");
}
}
return report.ToString();
}
/// <summary>
/// 获取优化摘要
/// </summary>
/// <returns>优化摘要文本</returns>
public string GetSummary()
{
if (!Success)
{
return $"优化失败: {Message}";
}
return $"优化成功: 长度减少 {LengthReduction:F3}m ({LengthReductionPercentage:F1}%), " +
$"点数减少 {PointReduction} 个 ({PointReductionPercentage:F1}%), " +
$"共执行 {OptimizationSteps.Count} 个优化步骤";
}
}
#endregion
/// <summary>
/// 路径点标记类型
/// </summary>
public enum PathPointMarkerType
{
/// <summary>
/// 文本标注
/// </summary>
TextLabel
}
/// <summary>
/// 路径点3D标记信息
/// </summary>
public class PathPointMarker
{
/// <summary>
/// 关联的路径点
/// </summary>
public PathPoint PathPoint { get; set; }
/// <summary>
/// 标记类型
/// </summary>
public PathPointMarkerType MarkerType { get; set; }
/// <summary>
/// 标注文本
/// </summary>
public string LabelText { get; set; }
/// <summary>
/// 标注位置
/// </summary>
public Point3D LabelPosition { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedTime { get; set; } = DateTime.Now;
}
/// <summary>
/// 路径历史记录项
/// </summary>
[Serializable]
public class PathHistoryEntry
{
/// <summary>
/// 历史记录唯一标识符
/// </summary>
public string Id { get; set; }
/// <summary>
/// 关联的路径ID
/// </summary>
public string RouteId { get; set; }
/// <summary>
/// 操作类型
/// </summary>
public PathHistoryOperationType OperationType { get; set; }
/// <summary>
/// 路径快照(操作前的状态)
/// </summary>
public PathRoute RouteSnapshot { get; set; }
/// <summary>
/// 操作时间
/// </summary>
public DateTime OperationTime { get; set; }
/// <summary>
/// 操作描述
/// </summary>
public string Description { get; set; }
/// <summary>
/// 版本号
/// </summary>
public int Version { get; set; }
public PathHistoryEntry()
{
Id = Guid.NewGuid().ToString();
OperationTime = DateTime.Now;
Description = string.Empty;
Version = 1;
}
public PathHistoryEntry(string routeId, PathHistoryOperationType operationType, PathRoute routeSnapshot, string description = "")
{
Id = Guid.NewGuid().ToString();
RouteId = routeId;
OperationType = operationType;
RouteSnapshot = routeSnapshot?.Clone(); // 创建副本
OperationTime = DateTime.Now;
Description = description;
Version = 1;
}
}
/// <summary>
/// 路径历史操作类型
/// </summary>
public enum PathHistoryOperationType
{
/// <summary>
/// 创建路径
/// </summary>
Created,
/// <summary>
/// 编辑路径
/// </summary>
Edited,
/// <summary>
/// 删除路径点
/// </summary>
PointRemoved,
/// <summary>
/// 添加路径点
/// </summary>
PointAdded,
/// <summary>
/// 路径优化
/// </summary>
Optimized,
/// <summary>
/// 手动保存
/// </summary>
ManualSave
}
/// <summary>
/// 路径历史管理器
/// </summary>
public class PathHistoryManager
{
private Dictionary<string, List<PathHistoryEntry>> _routeHistories;
private int _maxHistoryCount;
/// <summary>
/// 历史记录变更事件
/// </summary>
public event EventHandler<PathHistoryEntry> HistoryEntryAdded;
public PathHistoryManager(int maxHistoryCount = 50)
{
_routeHistories = new Dictionary<string, List<PathHistoryEntry>>();
_maxHistoryCount = maxHistoryCount;
}
/// <summary>
/// 保存路径到历史记录
/// </summary>
public void SaveToHistory(PathRoute route)
{
if (route != null)
{
var entry = new PathHistoryEntry(route.Id, PathHistoryOperationType.ManualSave, route, "手动保存");
AddHistoryEntry(entry);
}
}
/// <summary>
/// 添加历史记录
/// </summary>
public void AddHistoryEntry(PathHistoryEntry entry)
{
if (string.IsNullOrEmpty(entry.RouteId)) return;
if (!_routeHistories.ContainsKey(entry.RouteId))
{
_routeHistories[entry.RouteId] = new List<PathHistoryEntry>();
}
var histories = _routeHistories[entry.RouteId];
// 设置版本号
entry.Version = histories.Count + 1;
histories.Add(entry);
// 限制历史记录数量
if (histories.Count > _maxHistoryCount)
{
histories.RemoveAt(0);
// 重新计算版本号
for (int i = 0; i < histories.Count; i++)
{
histories[i].Version = i + 1;
}
}
HistoryEntryAdded?.Invoke(this, entry);
}
/// <summary>
/// 获取路径的历史记录
/// </summary>
public List<PathHistoryEntry> GetRouteHistory(string routeId)
{
return _routeHistories.ContainsKey(routeId) ?
new List<PathHistoryEntry>(_routeHistories[routeId]) :
new List<PathHistoryEntry>();
}
/// <summary>
/// 获取最新的历史记录
/// </summary>
public PathHistoryEntry GetLatestHistory(string routeId)
{
var histories = GetRouteHistory(routeId);
return histories.LastOrDefault();
}
/// <summary>
/// 清理路径的历史记录
/// </summary>
public void ClearRouteHistory(string routeId)
{
if (_routeHistories.ContainsKey(routeId))
{
_routeHistories.Remove(routeId);
}
}
/// <summary>
/// 获取所有路径的历史记录统计
/// </summary>
public Dictionary<string, int> GetHistoryStatistics()
{
return _routeHistories.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value.Count
);
}
/// <summary>
/// 获取所有历史记录
/// </summary>
/// <returns>所有历史记录的列表,按时间倒序排列</returns>
public List<PathHistoryEntry> GetAllHistoryEntries()
{
var allEntries = new List<PathHistoryEntry>();
foreach (var routeHistory in _routeHistories.Values)
{
allEntries.AddRange(routeHistory);
}
// 按操作时间倒序排列(最新的在前)
return allEntries.OrderByDescending(entry => entry.OperationTime).ToList();
}
}
/// <summary>
/// 路径文件序列化帮助类
/// </summary>
public static class PathFileSerializer
{
/// <summary>
/// 将路径保存为XML文件
/// </summary>
public static bool SaveToXml(PathRoute route, string filePath)
{
try
{
var serializer = new XmlSerializer(typeof(PathRoute));
using (var writer = new StreamWriter(filePath, false, Encoding.UTF8))
{
serializer.Serialize(writer, route);
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"保存XML文件失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 从XML文件加载路径
/// </summary>
public static PathRoute LoadFromXml(string filePath)
{
try
{
var serializer = new XmlSerializer(typeof(PathRoute));
using (var reader = new StreamReader(filePath, Encoding.UTF8))
{
return (PathRoute)serializer.Deserialize(reader);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"加载XML文件失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 将路径保存为JSON文件简化版
/// </summary>
public static bool SaveToJson(PathRoute route, string filePath)
{
try
{
// 使用简单的字符串格式化代替JSON序列化
var json = new StringBuilder();
json.AppendLine("{");
json.AppendLine($" \"Id\": \"{route.Id}\",");
json.AppendLine($" \"Name\": \"{route.Name}\",");
json.AppendLine($" \"Description\": \"{route.Description}\",");
json.AppendLine($" \"CreatedTime\": \"{route.CreatedTime:yyyy-MM-dd HH:mm:ss}\",");
json.AppendLine($" \"TotalLength\": {route.TotalLength},");
json.AppendLine($" \"EstimatedTime\": {route.EstimatedTime},");
json.AppendLine(" \"Points\": [");
for (int i = 0; i < route.Points.Count; i++)
{
var point = route.Points[i];
json.AppendLine(" {");
json.AppendLine($" \"Id\": \"{point.Id}\",");
json.AppendLine($" \"Name\": \"{point.Name}\",");
json.AppendLine($" \"Type\": \"{point.Type}\",");
json.AppendLine($" \"Position\": {{\"X\": {point.Position.X}, \"Y\": {point.Position.Y}, \"Z\": {point.Position.Z}}},");
json.AppendLine($" \"CreatedTime\": \"{point.CreatedTime:yyyy-MM-dd HH:mm:ss}\",");
json.AppendLine($" \"Index\": {point.Index},");
json.AppendLine($" \"Notes\": \"{point.Notes}\"");
json.AppendLine(i < route.Points.Count - 1 ? " }," : " }");
}
json.AppendLine(" ]");
json.AppendLine("}");
File.WriteAllText(filePath, json.ToString(), Encoding.UTF8);
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"保存JSON文件失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 从JSON文件加载路径简化版仅支持基本格式
/// </summary>
public static PathRoute LoadFromJson(string filePath)
{
try
{
// 简化实现仅支持基本的JSON格式
// 由于不依赖外部JSON库这里使用XML格式作为fallback
System.Diagnostics.Debug.WriteLine("JSON加载功能简化建议使用XML格式");
return null;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"加载JSON文件失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 将多个路径保存为XML文件
/// </summary>
public static bool SaveRoutesToXml(List<PathRoute> routes, string filePath)
{
try
{
var container = new PathRouteContainer { Routes = routes };
var serializer = new XmlSerializer(typeof(PathRouteContainer));
using (var writer = new StreamWriter(filePath, false, Encoding.UTF8))
{
serializer.Serialize(writer, container);
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"保存多路径XML文件失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 从XML文件加载多个路径
/// </summary>
public static List<PathRoute> LoadRoutesFromXml(string filePath)
{
try
{
var serializer = new XmlSerializer(typeof(PathRouteContainer));
using (var reader = new StreamReader(filePath, Encoding.UTF8))
{
var container = (PathRouteContainer)serializer.Deserialize(reader);
return container?.Routes ?? new List<PathRoute>();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"加载多路径XML文件失败: {ex.Message}");
return new List<PathRoute>();
}
}
/// <summary>
/// 检测文件格式
/// </summary>
public static string DetectFileFormat(string filePath)
{
var extension = Path.GetExtension(filePath).ToLowerInvariant();
switch (extension)
{
case ".xml":
return "XML";
case ".json":
return "JSON";
default:
// 尝试检测内容
try
{
var content = File.ReadAllText(filePath).Trim();
if (content.StartsWith("<") && content.EndsWith(">"))
return "XML";
if (content.StartsWith("{") && content.EndsWith("}"))
return "JSON";
}
catch (Exception ex)
{
LogManager.Debug($"[路径格式检测] 读取文件失败: {ex.Message}");
}
return "未知";
}
}
}
/// <summary>
/// 路径容器(用于保存多个路径)
/// </summary>
[Serializable]
public class PathRouteContainer
{
public List<PathRoute> Routes { get; set; } = new List<PathRoute>();
public DateTime CreatedTime { get; set; } = DateTime.Now;
public string Version { get; set; } = "1.0";
public string Description { get; set; } = "";
}
#region
/// <summary>
/// 吊装路径模式枚举
/// </summary>
public enum HoistingMode
{
/// <summary>
/// 单层吊装(传统模式,向后兼容)
/// </summary>
SingleLevel,
/// <summary>
/// 多层阶梯式吊装
/// </summary>
MultiLevel
}
/// <summary>
/// 吊装层级信息 - 表示多层吊装中的一个水平移动层级
/// </summary>
[Serializable]
public class HoistingLevel
{
/// <summary>
/// 层级序号从0开始
/// </summary>
public int Index { get; set; }
/// <summary>
/// 该层级的高度(相对于起吊点的垂直高度,米)
/// </summary>
public double HeightInMeters { get; set; }
/// <summary>
/// 该层级的水平移动目标点XY位置Z坐标会被忽略
/// </summary>
public Point3D HorizontalTarget { get; set; }
/// <summary>
/// 运动方向true=从上一位置上升到此高度false=从上一位置下降到此高度
/// </summary>
public bool IsRise { get; set; }
/// <summary>
/// 层级描述(如"桁车移动层"、"避让层"等)
/// </summary>
public string Description { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedTime { get; set; }
/// <summary>
/// 默认构造函数
/// </summary>
public HoistingLevel()
{
Index = 0;
HeightInMeters = 0.0;
HorizontalTarget = new Point3D();
IsRise = true;
Description = string.Empty;
CreatedTime = DateTime.Now;
}
/// <summary>
/// 带参数构造函数
/// </summary>
/// <param name="index">层级序号</param>
/// <param name="heightInMeters">高度(米)</param>
/// <param name="horizontalTarget">水平目标点</param>
/// <param name="isRise">是否为上升</param>
/// <param name="description">描述</param>
public HoistingLevel(int index, double heightInMeters, Point3D horizontalTarget, bool isRise = true, string description = "")
{
Index = index;
HeightInMeters = heightInMeters;
HorizontalTarget = horizontalTarget ?? new Point3D();
IsRise = isRise;
Description = description ?? string.Empty;
CreatedTime = DateTime.Now;
}
/// <summary>
/// 返回层级描述字符串
/// </summary>
public override string ToString()
{
string direction = IsRise ? "上升" : "下降";
return $"层级{Index + 1}: {direction}到{HeightInMeters:F2}米,目标({HorizontalTarget.X:F2}, {HorizontalTarget.Y:F2}) {(string.IsNullOrEmpty(Description) ? "" : $"- {Description}")}";
}
}
#endregion
}