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 { /// /// 路径编辑状态枚举 /// public enum PathEditState { /// /// 无状态 - 默认状态 /// None, /// /// 查看状态 - 只能查看路径,不能编辑 /// Viewing, /// /// 新建状态 - 正在创建新路径 /// Creating, /// /// 编辑状态 - 正在编辑现有路径 /// Editing, /// /// 添加路径点状态 - 正在添加路径点 /// AddingPoints, /// /// 修改路径点状态 - 正在修改现有路径点 /// EditingPoint } /// /// 路径类型枚举 - 分离空轨和吊装为独立类型 /// public enum PathType { /// /// 地面路径 - 物体在地面运行 /// Ground = 0, /// /// 空轨路径 - 物体沿空轨移动(可能有坡度),可以获取中心线并吸附 /// Rail = 1, /// /// 吊装路径 - 起点吊起 → 平移 → 终点吊下 /// Hoisting = 2 } /// /// PathType枚举扩展方法 /// public static class PathTypeExtensions { /// /// 获取路径类型的中文名称 /// public static string GetDisplayName(this PathType pathType) { switch (pathType) { case PathType.Ground: return "地面"; case PathType.Rail: return "空轨"; case PathType.Hoisting: return "吊装"; default: return "未知"; } } } /// /// 吊装路径点方向类型 /// 用于标识吊装路径中每个路径点的移动方向 /// public enum HoistingPointDirection { /// /// 垂直移动(起吊/下降) /// Vertical, /// /// 纵向移动(X轴方向) /// Longitudinal, /// /// 横向移动(Y轴方向) /// Lateral } /// /// Rail 路径安装方式 /// public enum RailMountMode { /// /// 轨下安装 /// UnderRail = 0, /// /// 轨上安装 /// OverRail = 1 } /// /// Rail 路径的参考线定义方式 /// public enum RailPathDefinitionMode { /// /// 旧版兼容模式:路径点即轨下悬挂参考点 /// LegacySuspensionPoint = 0, /// /// 新版模式:路径点表示双轨参考中心线 /// RailCenterLine = 1 } /// /// 通道检测结果 /// public class ChannelDetectionResult { /// /// 是否为有效位置 /// public bool IsValidLocation { get; set; } /// /// 检测消息 /// public string Message { get; set; } /// /// 检测方法 /// public string DetectionMethod { get; set; } } /// /// 物流通道检测结果 /// public class LogisticsChannelDetectionResult { /// /// 是否为有效通道 /// public bool IsValidChannel { get; set; } /// /// 通道类型名称 /// public string ChannelType { get; set; } /// /// 检测到的通道模型项 /// public ModelItem DetectedChannel { get; set; } /// /// 物流类别 /// public CategoryAttributeManager.LogisticsElementType? LogisticsCategory { get; set; } /// /// 检测置信度 /// public double DetectionConfidence { get; set; } /// /// 错误消息 /// public string ErrorMessage { get; set; } } /// /// 几何分析结果 /// public class GeometryAnalysisResult { /// /// 是否可能是通道 /// public bool IsLikelyChannel { get; set; } /// /// 分析原因 /// public string Reason { get; set; } /// /// 最可能的通道模型项 /// public ModelItem MostLikelyChannel { get; set; } /// /// 置信度 /// public double Confidence { get; set; } } /// /// 路径规划策略枚举 /// public enum PathStrategy { /// /// 最短路径 - 标准A*算法,所有边具有相同权重(默认策略) /// Shortest = 0, /// /// 直线优先路径 - 优先选择主方向的路径,减少转弯次数 /// Straightest = 1, /// /// 安全优先路径 - 基于障碍物距离选择安全路径,适合大型物体居中行驶 /// SafestCenter = 2 } /// /// 路径点类型枚举 /// public enum PathPointType { /// /// 起点 /// StartPoint, /// /// 终点 /// EndPoint, /// /// 路径点 /// WayPoint, /// /// 中间点 /// IntermediatePoint } /// /// 路径点数据模型 /// [Serializable] public class PathPoint { /// /// 路径点唯一标识符 /// public string Id { get; set; } /// /// 路径点3D位置(不参与XML序列化) /// [XmlIgnore] public Point3D Position { get; set; } /// /// X坐标(用于XML序列化) /// [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); } } /// /// Y坐标(用于XML序列化) /// [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); } } /// /// Z坐标(用于XML序列化) /// [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); } } /// /// 路径点名称 /// public string Name { get; set; } /// /// 路径点类型 /// public PathPointType Type { get; set; } /// /// 创建时间 /// public DateTime CreatedTime { get; set; } /// /// 路径点序号 /// public int Index { get; set; } /// /// 备注信息 /// public string Notes { get; set; } /// /// 限速(米/秒),0表示未设置限速 /// public double SpeedLimit { get; set; } /// /// 自定义转向半径(模型单位),用于局部急转弯场景 /// 为 null 时使用全局默认值 /// public double? CustomTurnRadius { get; set; } /// /// 仅用于视图显示的点直径(模型单位)。 /// 为 null 或非正数时使用默认点类型尺寸。 /// [XmlIgnore] public double? VisualizationDiameter { get; set; } /// /// 方向类型(仅吊装路径使用) /// 用于标识吊装路径点的移动方向:垂直、纵向(X轴)、横向(Y轴) /// public HoistingPointDirection Direction { get; set; } = HoistingPointDirection.Vertical; /// /// 构造函数 /// 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; } /// /// 带参数构造函数 /// /// 3D坐标 /// 名称 /// 类型 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; } /// /// 解析最终用于渲染的点半径(模型单位)。 /// /// 默认点半径(模型单位) /// 最终半径(模型单位) public double ResolveVisualizationRadius(double defaultRadius) { if (!VisualizationDiameter.HasValue || VisualizationDiameter.Value <= 0) { return defaultRadius; } return VisualizationDiameter.Value * 0.5; } /// /// 返回路径点描述字符串 /// /// public override string ToString() { return $"{Name} ({Type}) - X:{Position.X:F2}, Y:{Position.Y:F2}, Z:{Position.Z:F2}"; } } /// /// 路径段类型 /// public enum PathSegmentType { /// /// 直线段 /// Straight, /// /// 圆弧段 /// Arc } /// /// 圆弧轨迹数据 /// [Serializable] public class ArcTrajectory { /// /// 进入切点 /// public Point3D Ts { get; set; } /// /// 退出切点 /// public Point3D Te { get; set; } /// /// 圆心位置 /// public Point3D ArcCenter { get; set; } /// /// 请求半径(配置的转向半径) /// public double RequestedRadius { get; set; } /// /// 实际半径(安全截断后) /// public double ActualRadius { get; set; } /// /// 偏转角(弧度) /// public double DeflectionAngle { get; set; } /// /// 圆弧长度(米) /// public double ArcLength { get; set; } } /// /// 路径边 - 连接两个连续控制点的物理路径段 /// [Serializable] public class PathEdge { /// /// 边唯一标识符 /// public string Id { get; set; } /// /// 起始控制点ID /// public string StartPointId { get; set; } /// /// 结束控制点ID /// public string EndPointId { get; set; } /// /// 路径段类型 /// public PathSegmentType SegmentType { get; set; } /// /// 圆弧轨迹数据(仅当 SegmentType == Arc 时有效) /// public ArcTrajectory Trajectory { get; set; } /// /// 边的物理长度(米) /// 直线段:两点间距离;圆弧段:直线段长度 + 圆弧长度 /// public double PhysicalLength { get; set; } /// /// 采样点序列(用于碰撞检测和动画) /// [XmlIgnore] public List SampledPoints { get; set; } public PathEdge() { Id = Guid.NewGuid().ToString(); SampledPoints = new List(); } } /// /// 路径路线数据模型 /// [Serializable] public class PathRoute { public static bool IsTopReferenceFaceForMountMode(RailMountMode railMountMode) { return railMountMode == RailMountMode.UnderRail; } /// /// 路径点集合(控制点) /// public List Points { get; set; } /// /// 路径边集合 - 存储物理路径段 /// public List Edges { get; set; } /// /// 路径名称 /// public string Name { get; set; } /// /// 路径ID /// public string Id { get; set; } /// /// 预估时间(秒) /// public double EstimatedTime { get; set; } /// /// 关联通道模型ID集合 /// public List AssociatedChannelIds { get; set; } /// /// 路径总长度(米)- 只读计算属性 /// /// 【重要】此属性实时从几何数据计算,不从数据库存储或加载。 /// 数据库中的 TotalLength 字段已废弃,保留仅用于兼容旧版本。 /// /// 计算方式: /// - 地面路径(Ground):从 Edges.PhysicalLength 累加(支持圆弧) /// - 空轨路径(Rail):从 Points 计算直线距离 /// - 吊装路径(Hoisting):从 Points 计算直线距离 /// 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(); } } /// /// 从路径点计算直线距离总长度(辅助方法) /// 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; } /// /// 创建时间 /// public DateTime CreatedTime { get; set; } /// /// 最后修改时间 /// public DateTime LastModified { get; set; } /// /// 路径描述 /// public string Description { get; set; } /// /// 路径是否完整到达终点 /// public bool IsComplete { get; set; } /// /// 原始终点坐标(用于显示灰色未到达点) /// public Point3D OriginalEndPoint { get; set; } /// /// 实际到达点坐标 /// public Point3D ActualEndPoint { get; set; } /// /// 完成百分比(0-100) /// public double CompletionPercentage { get; set; } /// /// 关联的网格地图(用于网格可视化) /// [XmlIgnore] public NavisworksTransport.PathPlanning.GridMap AssociatedGridMap { get; set; } /// /// 网格大小(米) /// public double GridSize { get; set; } /// /// 最大物体长度(米) - 路径适用的物体长度限制 /// public double MaxObjectLength { get; set; } /// /// 最大物体宽度(米) - 路径适用的物体宽度限制 /// public double MaxObjectWidth { get; set; } /// /// 最大物体高度(米) - 路径适用的物体高度限制 /// public double MaxObjectHeight { get; set; } /// /// 安全间隙(米) /// public double SafetyMargin { get; set; } /// /// 路径转弯半径(模型单位)- 路径允许的最大转弯半径 /// 物体的最小转弯半径必须小于等于此值 /// public double TurnRadius { get; set; } /// /// 是否已曲线化 /// public bool IsCurved { get; set; } /// /// 路径类型 /// public PathType PathType { get; set; } = PathType.Ground; /// /// 吊装的提升高度(模型单位,仅当 PathType == Hoisting 时有效) /// public double LiftHeight { get; set; } /// /// Rail 路径安装方式(仅当 PathType == Rail 时有效) /// public RailMountMode RailMountMode { get; set; } /// /// Rail 路径参考线定义方式(仅当 PathType == Rail 时有效) /// public RailPathDefinitionMode RailPathDefinitionMode { get; set; } /// /// Rail 路径优先法向(宿主坐标)。 /// 仅在存在显式安装面/业务法向时使用;为空时退回当前默认求解。 /// public Point3D RailPreferredNormal { get; set; } /// /// Rail 安装法向偏移(模型单位)。 /// 沿 Rail 最终法向对安装参考点施加额外偏移,用于快速微调路径。 /// public double RailNormalOffset { get; set; } // 数据库分析相关属性 /// /// 碰撞数量(从数据库加载) /// public int? CollisionCount { get; set; } /// /// 安全评分(从数据库加载) /// public double? SafetyScore { get; set; } /// /// 效率评分(从数据库加载) /// public double? EfficiencyScore { get; set; } /// /// 综合评分(从数据库加载) /// public double? OverallScore { get; set; } /// /// 构造函数 /// public PathRoute() { Points = new List(); Edges = new List(); Name = string.Empty; Id = Guid.NewGuid().ToString(); EstimatedTime = 0.0; AssociatedChannelIds = new List(); // 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; } /// /// 带参数构造函数 /// /// 路径名称 public PathRoute(string name) { Points = new List(); Edges = new List(); Name = name; Id = Guid.NewGuid().ToString(); EstimatedTime = 0.0; AssociatedChannelIds = new List(); // 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; } /// /// 添加路径点 /// /// 路径点 public void AddPoint(PathPoint point) { Points.Add(point); // 调用统一更新函数(内部会重新设置索引) RecalculateAndSaveRoute("添加路径点"); } /// /// 在指定位置插入路径点 /// /// 要插入的路径点 /// 插入位置索引 public void InsertPoint(PathPoint point, int insertIndex) { Points.Insert(insertIndex, point); // 调用统一更新函数(内部会重新设置索引) RecalculateAndSaveRoute("插入路径点"); } /// /// 移除路径点 /// /// 路径点 public bool RemovePoint(PathPoint point) { bool removed = Points.Remove(point); if (removed) { // 调用统一更新函数(内部会重新设置索引) RecalculateAndSaveRoute("移除路径点"); } return removed; } /// /// 根据ID移除路径点 /// /// 路径点ID /// 是否成功移除 public bool RemovePoint(string pointId) { var point = Points.FirstOrDefault(p => p.Id == pointId); if (point != null) { return RemovePoint(point); } return false; } /// /// 清空所有路径点 /// public void ClearPoints() { Points.Clear(); Edges.Clear(); IsCurved = false; // 调用统一更新函数 RecalculateAndSaveRoute("清空路径点"); } /// /// 获取起点 /// /// public PathPoint GetStartPoint() { return Points.Find(p => p.Type == PathPointType.StartPoint); } /// /// 获取终点 /// /// public PathPoint GetEndPoint() { return Points.Find(p => p.Type == PathPointType.EndPoint); } /// /// 获取所有路径点(按序号排序) /// /// public List GetSortedPoints() { var sortedPoints = new List(Points); sortedPoints.Sort((p1, p2) => p1.Index.CompareTo(p2.Index)); return sortedPoints; } /// /// 重新计算路径数据(不保存到数据库) /// 用于路径优化等临时计算场景 /// /// 上下文信息(用于日志) 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 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); } } /// /// 重新计算路径数据并保存到数据库 /// 统一的路径数据更新入口:重新设置索引、更新长度、计算曲线、保存到数据库 /// /// 上下文信息(用于日志) 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); } } /// /// 验证路径有效性 /// /// 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; } /// /// 克隆路径 /// /// 克隆的路径对象 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(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(); // 克隆所有路径点 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(edge.SampledPoints) : new List() }; clonedRoute.Edges.Add(clonedEdge); } return clonedRoute; } /// /// 返回路径描述字符串 /// /// public override string ToString() { return $"{Name} - 点数:{Points.Count}, 长度:{TotalLength:F2}m, 预估时间:{EstimatedTime:F1}s"; } } /// /// 2D地图坐标 /// [Serializable] public class MapPoint2D { /// /// X坐标 /// public double X { get; set; } /// /// Y坐标 /// public double Y { get; set; } /// /// 构造函数 /// public MapPoint2D() { X = 0.0; Y = 0.0; } /// /// 带参数构造函数 /// /// X坐标 /// Y坐标 public MapPoint2D(double x, double y) { X = x; Y = y; } /// /// 返回坐标描述字符串 /// /// public override string ToString() { return $"({X:F2}, {Y:F2})"; } } /// /// 通道边界信息 /// [Serializable] public class ChannelBounds { /// /// 最小坐标点 /// public Point3D MinPoint { get; set; } /// /// 最大坐标点 /// public Point3D MaxPoint { get; set; } /// /// 中心点 /// public Point3D Center { get; set; } /// /// 通道高度(Z轴方向) /// public double Height { get; set; } /// /// 构造函数 /// public ChannelBounds() { MinPoint = new Point3D(); MaxPoint = new Point3D(); Center = new Point3D(); Height = 0.0; } /// /// 带包围盒参数构造函数 /// /// Navisworks包围盒 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; } /// /// 获取2D投影范围 /// /// 最小点 /// 最大点 public void Get2DProjection(out MapPoint2D min, out MapPoint2D max) { min = new MapPoint2D(MinPoint.X, MinPoint.Y); max = new MapPoint2D(MaxPoint.X, MaxPoint.Y); } } /// /// 通道选择结果 /// public class ChannelSelectionResult { /// /// 是否成功 /// public bool Success { get; set; } = false; /// /// 结果消息 /// public string Message { get; set; } = ""; /// /// 总模型项数量 /// public int TotalModelItems { get; set; } = 0; /// /// 自动检测的通道 /// public ModelItemCollection AutoDetectedChannels { get; set; } = new ModelItemCollection(); /// /// 已标记物流属性的模型项 /// public ModelItemCollection LogisticsMarkedItems { get; set; } = new ModelItemCollection(); /// /// 可通行区域 /// public ModelItemCollection TraversableAreas { get; set; } = new ModelItemCollection(); /// /// 通道类型模型项 /// public ModelItemCollection ChannelItems { get; set; } = new ModelItemCollection(); } /// /// 通道选择对话框结果 /// public class ChannelSelectionDialogResult { /// /// 是否成功 /// public bool Success { get; set; } = false; /// /// 结果消息 /// public string Message { get; set; } = ""; /// /// 选中的通道 /// public ModelItemCollection SelectedChannels { get; set; } = new ModelItemCollection(); /// /// 选择方法 /// public ChannelSelectionMethod SelectionMethod { get; set; } = ChannelSelectionMethod.Manual; /// /// 高度限制(米) /// public double HeightLimit { get; set; } = 3.0; } /// /// 通道选择方法枚举 /// public enum ChannelSelectionMethod { /// /// 手动选择 /// Manual, /// /// 自动检测 /// AutoDetect, /// /// 基于物流属性 /// LogisticsAttribute, /// /// 可通行区域 /// TraversableAreas, /// /// 通道类型 /// ChannelType } /// /// 通道选择对话框 /// 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"; /// /// 选中的通道 /// public ModelItemCollection SelectedChannels { get { return _selectedChannels; } } /// /// 选择方法 /// public ChannelSelectionMethod SelectionMethod { get { return _selectionMethod; } } /// /// 高度限制(米) /// public double HeightLimit { get { return _heightLimit; } } /// /// 构造函数 /// /// 通道分析结果 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; /// /// 初始化对话框组件 /// 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); } /// /// 创建通道列表选项卡 /// 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); } /// /// 创建筛选选项卡 /// 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); } /// /// 加载分析数据 /// 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}"; } } } /// /// 更新统计信息 /// 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 事件处理方法 /// /// 预览按钮点击事件 /// 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); } } /// /// 清除高亮按钮点击事件 /// 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); } } /// /// 确定按钮点击事件 /// 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); } } /// /// 全选复选框状态改变事件 /// 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); } } /// /// 通道列表项选中状态改变事件 /// private void ChannelListView_ItemChecked(object sender, System.Windows.Forms.ItemCheckedEventArgs e) { UpdateStatistics(); } /// /// 高度限制数值改变事件 /// private void HeightLimitNumericUpDown_ValueChanged(object sender, EventArgs e) { _heightLimit = (double)heightLimitNumericUpDown.Value; } /// /// 筛选选项改变事件 /// private void FilterOption_CheckedChanged(object sender, EventArgs e) { // 可以根据需要实现筛选逻辑 } /// /// 应用筛选按钮点击事件 /// 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 路径验证与优化相关数据结构 /// /// 路径验证结果 /// public class PathValidationResult { /// /// 路径ID /// public string RouteId { get; set; } = ""; /// /// 路径名称 /// public string RouteName { get; set; } = ""; /// /// 验证是否通过 /// public bool IsValid { get; set; } = false; /// /// 验证消息 /// public string Message { get; set; } = ""; /// /// 错误列表 /// public List Errors { get; set; } = new List(); /// /// 警告列表 /// public List Warnings { get; set; } = new List(); /// /// 验证时间 /// public DateTime ValidationTime { get; set; } = DateTime.Now; /// /// 添加错误信息 /// /// 错误信息 public void AddError(string error) { if (!string.IsNullOrEmpty(error) && !Errors.Contains(error)) { Errors.Add(error); } } /// /// 添加警告信息 /// /// 警告信息 public void AddWarning(string warning) { if (!string.IsNullOrEmpty(warning) && !Warnings.Contains(warning)) { Warnings.Add(warning); } } /// /// 获取详细验证报告 /// /// 验证报告文本 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(); } } /// /// 路径优化选项 /// public class PathOptimizationOptions { /// /// 移除重复点 /// public bool RemoveDuplicatePoints { get; set; } = true; /// /// 路径平滑化 /// public bool SmoothPath { get; set; } = true; /// /// 优化路径角度 /// public bool OptimizeAngles { get; set; } = true; /// /// 调整到通道中心 /// public bool AdjustToChannels { get; set; } = false; /// /// 重复点检测阈值(米) /// public double DuplicatePointThreshold { get; set; } = 0.01; /// /// 角度优化容差(弧度) /// public double AngleOptimizationTolerance { get; set; } = 0.1; /// /// 平滑强度(0-1) /// public double SmoothingStrength { get; set; } = 0.5; /// /// 创建默认优化选项 /// /// 默认优化选项 public static PathOptimizationOptions CreateDefault() { return new PathOptimizationOptions { RemoveDuplicatePoints = true, SmoothPath = true, OptimizeAngles = true, AdjustToChannels = false, DuplicatePointThreshold = 0.01, AngleOptimizationTolerance = 0.1, SmoothingStrength = 0.5 }; } /// /// 创建保守优化选项(仅基础优化) /// /// 保守优化选项 public static PathOptimizationOptions CreateConservative() { return new PathOptimizationOptions { RemoveDuplicatePoints = true, SmoothPath = false, OptimizeAngles = false, AdjustToChannels = false, DuplicatePointThreshold = 0.001 }; } /// /// 创建激进优化选项(所有优化) /// /// 激进优化选项 public static PathOptimizationOptions CreateAggressive() { return new PathOptimizationOptions { RemoveDuplicatePoints = true, SmoothPath = true, OptimizeAngles = true, AdjustToChannels = true, DuplicatePointThreshold = 0.05, AngleOptimizationTolerance = 0.2, SmoothingStrength = 0.8 }; } } /// /// 路径优化结果 /// public class PathOptimizationResult { /// /// 优化是否成功 /// public bool Success { get; set; } = false; /// /// 优化消息 /// public string Message { get; set; } = ""; /// /// 原始路径 /// public PathRoute OriginalRoute { get; set; } /// /// 优化后的路径 /// public PathRoute OptimizedRoute { get; set; } /// /// 原始路径长度 /// public double OriginalLength { get; set; } = 0.0; /// /// 优化后路径长度 /// public double OptimizedLength { get; set; } = 0.0; /// /// 长度减少量 /// public double LengthReduction { get; set; } = 0.0; /// /// 长度减少百分比 /// public double LengthReductionPercentage => OriginalLength > 0 ? (LengthReduction / OriginalLength) * 100 : 0; /// /// 原始点数 /// public int OriginalPointCount { get; set; } = 0; /// /// 优化后点数 /// public int OptimizedPointCount { get; set; } = 0; /// /// 点数减少量 /// public int PointReduction { get; set; } = 0; /// /// 点数减少百分比 /// public double PointReductionPercentage => OriginalPointCount > 0 ? ((double)PointReduction / OriginalPointCount) * 100 : 0; /// /// 优化步骤记录 /// public List OptimizationSteps { get; set; } = new List(); /// /// 优化时间 /// public DateTime OptimizationTime { get; set; } = DateTime.Now; /// /// 优化耗时(毫秒) /// public long ElapsedMilliseconds { get; set; } = 0; /// /// 添加优化步骤记录 /// /// 优化步骤描述 public void AddOptimizationStep(string step) { if (!string.IsNullOrEmpty(step)) { OptimizationSteps.Add($"[{DateTime.Now:HH:mm:ss}] {step}"); } } /// /// 获取详细优化报告 /// /// 优化报告文本 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(); } /// /// 获取优化摘要 /// /// 优化摘要文本 public string GetSummary() { if (!Success) { return $"优化失败: {Message}"; } return $"优化成功: 长度减少 {LengthReduction:F3}m ({LengthReductionPercentage:F1}%), " + $"点数减少 {PointReduction} 个 ({PointReductionPercentage:F1}%), " + $"共执行 {OptimizationSteps.Count} 个优化步骤"; } } #endregion /// /// 路径点标记类型 /// public enum PathPointMarkerType { /// /// 文本标注 /// TextLabel } /// /// 路径点3D标记信息 /// public class PathPointMarker { /// /// 关联的路径点 /// public PathPoint PathPoint { get; set; } /// /// 标记类型 /// public PathPointMarkerType MarkerType { get; set; } /// /// 标注文本 /// public string LabelText { get; set; } /// /// 标注位置 /// public Point3D LabelPosition { get; set; } /// /// 创建时间 /// public DateTime CreatedTime { get; set; } = DateTime.Now; } /// /// 路径历史记录项 /// [Serializable] public class PathHistoryEntry { /// /// 历史记录唯一标识符 /// public string Id { get; set; } /// /// 关联的路径ID /// public string RouteId { get; set; } /// /// 操作类型 /// public PathHistoryOperationType OperationType { get; set; } /// /// 路径快照(操作前的状态) /// public PathRoute RouteSnapshot { get; set; } /// /// 操作时间 /// public DateTime OperationTime { get; set; } /// /// 操作描述 /// public string Description { get; set; } /// /// 版本号 /// 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; } } /// /// 路径历史操作类型 /// public enum PathHistoryOperationType { /// /// 创建路径 /// Created, /// /// 编辑路径 /// Edited, /// /// 删除路径点 /// PointRemoved, /// /// 添加路径点 /// PointAdded, /// /// 路径优化 /// Optimized, /// /// 手动保存 /// ManualSave } /// /// 路径历史管理器 /// public class PathHistoryManager { private Dictionary> _routeHistories; private int _maxHistoryCount; /// /// 历史记录变更事件 /// public event EventHandler HistoryEntryAdded; public PathHistoryManager(int maxHistoryCount = 50) { _routeHistories = new Dictionary>(); _maxHistoryCount = maxHistoryCount; } /// /// 保存路径到历史记录 /// public void SaveToHistory(PathRoute route) { if (route != null) { var entry = new PathHistoryEntry(route.Id, PathHistoryOperationType.ManualSave, route, "手动保存"); AddHistoryEntry(entry); } } /// /// 添加历史记录 /// public void AddHistoryEntry(PathHistoryEntry entry) { if (string.IsNullOrEmpty(entry.RouteId)) return; if (!_routeHistories.ContainsKey(entry.RouteId)) { _routeHistories[entry.RouteId] = new List(); } 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); } /// /// 获取路径的历史记录 /// public List GetRouteHistory(string routeId) { return _routeHistories.ContainsKey(routeId) ? new List(_routeHistories[routeId]) : new List(); } /// /// 获取最新的历史记录 /// public PathHistoryEntry GetLatestHistory(string routeId) { var histories = GetRouteHistory(routeId); return histories.LastOrDefault(); } /// /// 清理路径的历史记录 /// public void ClearRouteHistory(string routeId) { if (_routeHistories.ContainsKey(routeId)) { _routeHistories.Remove(routeId); } } /// /// 获取所有路径的历史记录统计 /// public Dictionary GetHistoryStatistics() { return _routeHistories.ToDictionary( kvp => kvp.Key, kvp => kvp.Value.Count ); } /// /// 获取所有历史记录 /// /// 所有历史记录的列表,按时间倒序排列 public List GetAllHistoryEntries() { var allEntries = new List(); foreach (var routeHistory in _routeHistories.Values) { allEntries.AddRange(routeHistory); } // 按操作时间倒序排列(最新的在前) return allEntries.OrderByDescending(entry => entry.OperationTime).ToList(); } } /// /// 路径文件序列化帮助类 /// public static class PathFileSerializer { /// /// 将路径保存为XML文件 /// 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; } } /// /// 从XML文件加载路径 /// 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; } } /// /// 将路径保存为JSON文件(简化版) /// 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; } } /// /// 从JSON文件加载路径(简化版,仅支持基本格式) /// 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; } } /// /// 将多个路径保存为XML文件 /// public static bool SaveRoutesToXml(List 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; } } /// /// 从XML文件加载多个路径 /// public static List 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(); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"加载多路径XML文件失败: {ex.Message}"); return new List(); } } /// /// 检测文件格式 /// 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 "未知"; } } } /// /// 路径容器(用于保存多个路径) /// [Serializable] public class PathRouteContainer { public List Routes { get; set; } = new List(); public DateTime CreatedTime { get; set; } = DateTime.Now; public string Version { get; set; } = "1.0"; public string Description { get; set; } = ""; } #region 多层阶梯式吊装路径数据模型 /// /// 吊装路径模式枚举 /// public enum HoistingMode { /// /// 单层吊装(传统模式,向后兼容) /// SingleLevel, /// /// 多层阶梯式吊装 /// MultiLevel } /// /// 吊装层级信息 - 表示多层吊装中的一个水平移动层级 /// [Serializable] public class HoistingLevel { /// /// 层级序号(从0开始) /// public int Index { get; set; } /// /// 该层级的高度(相对于起吊点的垂直高度,米) /// public double HeightInMeters { get; set; } /// /// 该层级的水平移动目标点(XY位置,Z坐标会被忽略) /// public Point3D HorizontalTarget { get; set; } /// /// 运动方向:true=从上一位置上升到此高度,false=从上一位置下降到此高度 /// public bool IsRise { get; set; } /// /// 层级描述(如"桁车移动层"、"避让层"等) /// public string Description { get; set; } /// /// 创建时间 /// public DateTime CreatedTime { get; set; } /// /// 默认构造函数 /// public HoistingLevel() { Index = 0; HeightInMeters = 0.0; HorizontalTarget = new Point3D(); IsRise = true; Description = string.Empty; CreatedTime = DateTime.Now; } /// /// 带参数构造函数 /// /// 层级序号 /// 高度(米) /// 水平目标点 /// 是否为上升 /// 描述 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; } /// /// 返回层级描述字符串 /// public override string ToString() { string direction = IsRise ? "上升" : "下降"; return $"层级{Index + 1}: {direction}到{HeightInMeters:F2}米,目标({HorizontalTarget.X:F2}, {HorizontalTarget.Y:F2}) {(string.IsNullOrEmpty(Description) ? "" : $"- {Description}")}"; } } #endregion }