NavisworksTransport/src/Core/PathDataManager.cs

1807 lines
79 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.IO;
using System.Xml;
using System.Text;
using System.Linq;
using System.Globalization;
using System.Web.Script.Serialization;
using Autodesk.Navisworks.Api;
namespace NavisworksTransport
{
#region JSON数据模型类
/// <summary>
/// JSON格式的路径点数据
/// </summary>
public class JsonPathPoint
{
public string id { get; set; }
public string name { get; set; }
public string type { get; set; }
public int index { get; set; }
public double x { get; set; }
public double y { get; set; }
public double z { get; set; }
public string created { get; set; }
}
/// <summary>
/// JSON格式的物体限制数据
/// </summary>
public class JsonObjectLimits
{
public double maxLength { get; set; }
public double maxWidth { get; set; }
public double maxHeight { get; set; }
public double safetyMargin { get; set; }
}
/// <summary>
/// JSON格式的路径数据
/// </summary>
public class JsonPathRoute
{
public string id { get; set; }
public string name { get; set; }
public string description { get; set; }
public string pathType { get; set; }
public string railMountMode { get; set; }
public string railPathDefinitionMode { get; set; }
public double railNormalOffset { get; set; }
public JsonVector3 railPreferredNormal { get; set; }
public double totalLength { get; set; }
public JsonObjectLimits objectLimits { get; set; }
public double gridSize { get; set; }
public double liftHeight { get; set; }
public string created { get; set; }
public JsonPathPoint[] points { get; set; }
}
public class JsonVector3
{
public double x { get; set; }
public double y { get; set; }
public double z { get; set; }
}
/// <summary>
/// JSON格式的项目信息
/// </summary>
public class JsonProjectInfo
{
public string name { get; set; }
public string description { get; set; }
public string units { get; set; }
public string coordinateSystem { get; set; }
}
/// <summary>
/// JSON格式的路径规划数据根对象
/// </summary>
public class JsonPathPlanningData
{
public string version { get; set; }
public string generator { get; set; }
public string timestamp { get; set; }
public JsonProjectInfo ProjectInfo { get; set; }
public JsonPathRoute[] Routes { get; set; }
}
/// <summary>
/// JSON格式的根对象
/// </summary>
public class JsonRootObject
{
public JsonPathPlanningData PathPlanningData { get; set; }
}
#endregion
/// <summary>
/// 路径数据管理器
/// 负责路径数据的导入导出、持久化存储和DELMIA格式兼容
/// </summary>
public class PathDataManager
{
private const string DEFAULT_EXPORT_FOLDER = "PathPlanningExports";
private const string DEFAULT_FILE_EXTENSION = ".xml";
private const string JSON_FILE_EXTENSION = ".json";
private const string CSV_FILE_EXTENSION = ".csv";
// DELMIA兼容格式配置
private readonly string _delmiaNamespace = "http://www.3ds.com/delmia/pathplanning";
private readonly string _delmiaVersion = "1.0";
/// <summary>
/// 导出路径到XML文件DELMIA兼容格式
/// </summary>
/// <param name="routes">路径集合</param>
/// <param name="filePath">文件路径</param>
/// <param name="exportSettings">导出设置</param>
/// <returns>是否成功</returns>
public bool ExportToXml(List<PathRoute> routes, string filePath, ExportSettings exportSettings = null)
{
if (routes == null || routes.Count == 0)
{
throw new ArgumentException("路径集合不能为空");
}
try
{
LogManager.Info($"[路径文件操作] 开始导出 {routes.Count} 条路径到XML文件: {filePath}");
// 确保导出目录存在
var directory = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// 创建XML文档
var xmlDoc = new XmlDocument();
var declaration = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null);
xmlDoc.AppendChild(declaration);
// 创建根元素
var rootElement = xmlDoc.CreateElement("PathPlanningData", _delmiaNamespace);
rootElement.SetAttribute("version", _delmiaVersion);
rootElement.SetAttribute("generator", "NavisworksTransport");
rootElement.SetAttribute("timestamp", DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss"));
xmlDoc.AppendChild(rootElement);
// 添加项目信息
var projectInfo = CreateProjectInfoElement(xmlDoc, exportSettings);
rootElement.AppendChild(projectInfo);
// 添加路径数据
var routesElement = xmlDoc.CreateElement("Routes", _delmiaNamespace);
rootElement.AppendChild(routesElement);
foreach (var route in routes)
{
if (route.IsValid())
{
var routeElement = CreateRouteElement(xmlDoc, route, exportSettings);
routesElement.AppendChild(routeElement);
}
}
// 保存文件
using (var writer = new XmlTextWriter(filePath, Encoding.UTF8))
{
writer.Formatting = Formatting.Indented;
writer.Indentation = 2;
xmlDoc.WriteTo(writer);
}
LogManager.Info($"[路径文件操作] 成功导出 {routes.Count} 条路径到XML文件: {filePath}");
return true;
}
catch (Exception ex)
{
LogManager.Error($"[路径文件操作] 导出XML文件失败: {ex.Message}", ex);
return false;
}
}
/// <summary>
/// 从XML文件导入路径
/// </summary>
/// <param name="filePath">文件路径</param>
/// <returns>路径集合</returns>
public List<PathRoute> ImportFromXml(string filePath)
{
var routes = new List<PathRoute>();
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"文件不存在: {filePath}");
}
LogManager.Info($"[路径文件操作] 开始从XML文件导入路径: {filePath}");
try
{
var xmlDoc = new XmlDocument();
xmlDoc.Load(filePath);
// 验证根元素
var rootElement = xmlDoc.DocumentElement;
if (rootElement?.Name != "PathPlanningData")
{
throw new InvalidOperationException("无效的路径规划数据文件格式");
}
// 读取路径数据
var routesElement = rootElement.SelectSingleNode("*[local-name()='Routes']");
if (routesElement != null)
{
foreach (XmlNode routeNode in routesElement.ChildNodes)
{
if (routeNode.NodeType == XmlNodeType.Element)
{
var route = ParseRouteElement(routeNode);
if (route != null && route.IsValid())
{
routes.Add(route);
}
}
}
}
LogManager.Info($"[路径文件操作] 成功从XML文件导入 {routes.Count} 条路径");
return routes;
}
catch (Exception ex)
{
LogManager.Error($"[路径文件操作] 导入XML文件失败: {ex.Message}", ex);
throw;
}
}
/// <summary>
/// 导出路径到JSON文件
/// </summary>
/// <param name="routes">路径集合</param>
/// <param name="filePath">文件路径</param>
/// <param name="exportSettings">导出设置</param>
/// <returns>是否成功</returns>
public bool ExportToJson(List<PathRoute> routes, string filePath, ExportSettings exportSettings = null)
{
if (routes == null || routes.Count == 0)
{
throw new ArgumentException("路径集合不能为空");
}
try
{
LogManager.Info($"[路径文件操作] 开始导出 {routes.Count} 条路径到JSON文件: {filePath}");
// 确保导出目录存在
var directory = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// 创建JSON数据结构
var jsonData = new
{
PathPlanningData = new
{
version = _delmiaVersion,
generator = "NavisworksTransport",
timestamp = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss"),
ProjectInfo = new
{
name = exportSettings?.ProjectName ?? "NavisworksTransport路径规划",
description = exportSettings?.Description ?? "物流路径规划数据",
units = exportSettings?.Units ?? "meters",
coordinateSystem = exportSettings?.CoordinateSystem ?? "Global"
},
Routes = routes.Where(r => r.IsValid()).Select(route => new
{
id = route.Id,
name = route.Name,
description = route.Description ?? "",
pathType = route.PathType.ToString(),
railMountMode = route.RailMountMode.ToString(),
railPathDefinitionMode = route.RailPathDefinitionMode.ToString(),
railNormalOffset = Math.Round(route.RailNormalOffset, exportSettings?.Precision ?? 3),
railPreferredNormal = route.RailPreferredNormal != null ? new
{
x = Math.Round(route.RailPreferredNormal.X, exportSettings?.Precision ?? 3),
y = Math.Round(route.RailPreferredNormal.Y, exportSettings?.Precision ?? 3),
z = Math.Round(route.RailPreferredNormal.Z, exportSettings?.Precision ?? 3)
} : null,
totalLength = Math.Round(route.TotalLength, exportSettings?.Precision ?? 3),
objectLimits = new
{
maxLength = Math.Round(route.MaxObjectLength, exportSettings?.Precision ?? 3),
maxWidth = Math.Round(route.MaxObjectWidth, exportSettings?.Precision ?? 3),
maxHeight = Math.Round(route.MaxObjectHeight, exportSettings?.Precision ?? 3),
safetyMargin = Math.Round(route.SafetyMargin, exportSettings?.Precision ?? 3)
},
gridSize = Math.Round(route.GridSize, exportSettings?.Precision ?? 3),
liftHeight = Math.Round(route.LiftHeight, exportSettings?.Precision ?? 3),
created = route.CreatedTime.ToString("yyyy-MM-ddTHH:mm:ss"),
points = route.GetSortedPoints().Select(point => new
{
id = point.Id,
name = point.Name,
type = point.Type.ToString(),
index = point.Index,
x = Math.Round(point.Position.X, exportSettings?.Precision ?? 3),
y = Math.Round(point.Position.Y, exportSettings?.Precision ?? 3),
z = Math.Round(point.Position.Z, exportSettings?.Precision ?? 3),
created = point.CreatedTime.ToString("yyyy-MM-ddTHH:mm:ss")
}).ToArray(),
edges = route.Edges != null && route.Edges.Count > 0 ? route.Edges.Select(edge => new
{
type = edge.SegmentType.ToString().ToLower(),
physicalLength = Math.Round(edge.PhysicalLength, exportSettings?.Precision ?? 3),
trajectory = edge.SegmentType == PathSegmentType.Arc && edge.Trajectory != null ? new
{
ts = new
{
x = Math.Round(edge.Trajectory.Ts.X, 3),
y = Math.Round(edge.Trajectory.Ts.Y, 3),
z = Math.Round(edge.Trajectory.Ts.Z, 3)
},
te = new
{
x = Math.Round(edge.Trajectory.Te.X, 3),
y = Math.Round(edge.Trajectory.Te.Y, 3),
z = Math.Round(edge.Trajectory.Te.Z, 3)
},
arcCenter = new
{
x = Math.Round(edge.Trajectory.ArcCenter.X, 3),
y = Math.Round(edge.Trajectory.ArcCenter.Y, 3),
z = Math.Round(edge.Trajectory.ArcCenter.Z, 3)
},
requestedRadius = Math.Round(edge.Trajectory.RequestedRadius, 3),
actualRadius = Math.Round(edge.Trajectory.ActualRadius, 3),
deflectionAngle = Math.Round(edge.Trajectory.DeflectionAngle, 3),
arcLength = Math.Round(edge.Trajectory.ArcLength, 3)
} : null
}).ToArray() : null
}).ToArray()
}
};
// 序列化为JSON
var serializer = new JavaScriptSerializer();
var jsonString = serializer.Serialize(jsonData);
// 格式化JSON输出简单的美化
var formattedJson = FormatJsonString(jsonString);
// 保存文件
File.WriteAllText(filePath, formattedJson, Encoding.UTF8);
LogManager.Info($"[路径文件操作] 成功导出 {routes.Count} 条路径到JSON文件: {filePath}");
return true;
}
catch (Exception ex)
{
LogManager.Error($"[路径文件操作] 导出JSON文件失败: {ex.Message}", ex);
return false;
}
}
/// <summary>
/// 导出路径到CSV文件
/// </summary>
/// <param name="routes">路径集合</param>
/// <param name="filePath">文件路径</param>
/// <param name="exportSettings">导出设置</param>
/// <returns>是否成功</returns>
public bool ExportToCsv(List<PathRoute> routes, string filePath, ExportSettings exportSettings = null)
{
if (routes == null || routes.Count == 0)
{
throw new ArgumentException("路径集合不能为空");
}
try
{
LogManager.Info($"[路径文件操作] 开始导出 {routes.Count} 条路径到CSV文件: {filePath}");
// 确保导出目录存在
var directory = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
using (var writer = new StreamWriter(filePath, false, Encoding.UTF8))
{
// 写入CSV表头采样点格式
writer.WriteLine("RouteId,EdgeIndex,SegmentType,PointIndex,X,Y,Z");
// 写入采样点数据行
foreach (var route in routes.Where(r => r.IsValid()))
{
if (route.Edges != null && route.Edges.Count > 0)
{
for (int edgeIndex = 0; edgeIndex < route.Edges.Count; edgeIndex++)
{
var edge = route.Edges[edgeIndex];
// 只导出已存在的采样点
if (edge.SampledPoints != null && edge.SampledPoints.Count > 0)
{
for (int pointIndex = 0; pointIndex < edge.SampledPoints.Count; pointIndex++)
{
var point = edge.SampledPoints[pointIndex];
var row = string.Join(",",
EscapeCsvValue(route.Id),
edgeIndex.ToString(),
edge.SegmentType.ToString(),
pointIndex.ToString(),
point.X.ToString("F3", CultureInfo.InvariantCulture),
point.Y.ToString("F3", CultureInfo.InvariantCulture),
point.Z.ToString("F3", CultureInfo.InvariantCulture)
);
writer.WriteLine(row);
}
}
}
}
}
}
LogManager.Info($"[路径文件操作] 成功导出路径到CSV文件: {filePath}");
return true;
}
catch (Exception ex)
{
LogManager.Error($"[路径文件操作] 导出CSV文件失败: {ex.Message}", ex);
return false;
}
}
/// <summary>
/// 从JSON文件导入路径
/// </summary>
/// <param name="filePath">文件路径</param>
/// <returns>路径集合</returns>
public List<PathRoute> ImportFromJson(string filePath)
{
if (string.IsNullOrEmpty(filePath))
{
throw new ArgumentException("文件路径不能为空", nameof(filePath));
}
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"找不到文件: {filePath}", filePath);
}
try
{
LogManager.Info($"[路径文件操作] 开始从JSON文件导入路径: {filePath}");
// 读取JSON文件
string jsonContent = File.ReadAllText(filePath, Encoding.UTF8);
// 反序列化JSON数据
var serializer = new JavaScriptSerializer();
var rootObject = serializer.Deserialize<JsonRootObject>(jsonContent);
if (rootObject?.PathPlanningData == null)
{
throw new InvalidDataException("JSON文件格式不正确缺少PathPlanningData节点");
}
var pathData = rootObject.PathPlanningData;
var routes = new List<PathRoute>();
if (pathData.Routes != null && pathData.Routes.Length > 0)
{
LogManager.Info($"发现 {pathData.Routes.Length} 条路径,开始导入...");
foreach (var jsonRoute in pathData.Routes)
{
try
{
// 验证路径数据完整性
if (string.IsNullOrEmpty(jsonRoute.id) || string.IsNullOrEmpty(jsonRoute.name))
{
LogManager.Warning($"跳过无效路径:缺少必要字段 (id或name)");
continue;
}
if (jsonRoute.points == null || jsonRoute.points.Length == 0)
{
LogManager.Warning($"跳过空路径: {jsonRoute.name}路径点数量为0");
continue;
}
// 创建路径对象
var route = new PathRoute(jsonRoute.name)
{
Id = jsonRoute.id,
Description = jsonRoute.description ?? "",
CreatedTime = ParseJsonDateTime(jsonRoute.created),
MaxObjectLength = jsonRoute.objectLimits?.maxLength ?? 0,
MaxObjectWidth = jsonRoute.objectLimits?.maxWidth ?? 0,
MaxObjectHeight = jsonRoute.objectLimits?.maxHeight ?? 0,
SafetyMargin = jsonRoute.objectLimits?.safetyMargin ?? 0,
GridSize = jsonRoute.gridSize,
LiftHeight = jsonRoute.liftHeight
};
// 🔥 解析路径类型(关键字段,不输则报错)
if (!string.IsNullOrEmpty(jsonRoute.pathType))
{
if (Enum.TryParse<PathType>(jsonRoute.pathType, out var pathType))
{
route.PathType = pathType;
}
else
{
LogManager.Error($"无法解析路径类型: {jsonRoute.pathType},路径: {jsonRoute.name}");
continue;
}
}
else
{
LogManager.Error($"路径类型缺失,路径: {jsonRoute.name}");
continue;
}
if (!string.IsNullOrEmpty(jsonRoute.railMountMode) &&
Enum.TryParse<RailMountMode>(jsonRoute.railMountMode, out var railMountMode))
{
route.RailMountMode = railMountMode;
}
if (!string.IsNullOrEmpty(jsonRoute.railPathDefinitionMode) &&
Enum.TryParse<RailPathDefinitionMode>(jsonRoute.railPathDefinitionMode, out var railPathDefinitionMode))
{
route.RailPathDefinitionMode = railPathDefinitionMode;
}
route.RailNormalOffset = jsonRoute.railNormalOffset;
if (jsonRoute.railPreferredNormal != null)
{
route.RailPreferredNormal = new Point3D(
jsonRoute.railPreferredNormal.x,
jsonRoute.railPreferredNormal.y,
jsonRoute.railPreferredNormal.z);
}
// 解析路径点
var points = new List<PathPoint>();
foreach (var jsonPoint in jsonRoute.points)
{
try
{
if (jsonPoint == null) continue;
// 验证路径点数据
if (double.IsNaN(jsonPoint.x) || double.IsNaN(jsonPoint.y) || double.IsNaN(jsonPoint.z))
{
LogManager.Warning($"跳过无效路径点坐标包含NaN值 (路径: {jsonRoute.name})");
continue;
}
// 解析路径点类型
if (!Enum.TryParse<PathPointType>(jsonPoint.type, out var pointType))
{
LogManager.Warning($"无法解析路径点类型: {jsonPoint.type},使用默认值: WayPoint");
pointType = PathPointType.WayPoint;
}
// 创建路径点
var point = new PathPoint(
new Point3D(jsonPoint.x, jsonPoint.y, jsonPoint.z),
jsonPoint.name ?? $"{pointType}_{jsonPoint.index}",
pointType
)
{
Id = jsonPoint.id ?? Guid.NewGuid().ToString("N"),
Index = jsonPoint.index,
CreatedTime = ParseJsonDateTime(jsonPoint.created)
};
points.Add(point);
}
catch (Exception ex)
{
LogManager.Warning($"[路径文件操作] 导入路径点失败: {ex.Message}");
// 继续处理其他路径点
}
}
if (points.Count == 0)
{
LogManager.Warning($"跳过没有有效路径点的路径: {jsonRoute.name}");
continue;
}
// 按index排序并添加到路径
points = points.OrderBy(p => p.Index).ToList();
foreach (var point in points)
{
route.AddPoint(point);
}
// 验证路径有效性
if (route.IsValid())
{
routes.Add(route);
LogManager.Info($"[路径文件操作] 成功导入路径: {route.Name},包含 {route.Points.Count} 个路径点");
}
else
{
LogManager.Warning($"跳过无效路径: {route.Name}");
}
}
catch (Exception ex)
{
LogManager.Error($"[路径文件操作] 导入路径失败: {jsonRoute.name ?? "Unknown"},错误: {ex.Message}");
// 继续处理其他路径
}
}
}
else
{
LogManager.Info("JSON文件中没有发现路径数据");
}
LogManager.Info($"JSON导入完成共导入 {routes.Count} 条有效路径");
return routes;
}
catch (FileNotFoundException)
{
LogManager.Error($"JSON导入失败文件不存在: {filePath}");
throw;
}
catch (InvalidOperationException ex) when (ex.Message.Contains("JSON"))
{
LogManager.Error($"JSON导入失败文件格式不正确或JSON语法错误: {filePath}");
throw new InvalidDataException("文件格式不正确或JSON语法错误", ex);
}
catch (Exception ex)
{
LogManager.Error($"JSON导入失败: {ex.Message}");
throw new InvalidOperationException($"JSON导入失败: {ex.Message}", ex);
}
}
/// <summary>
/// 解析JSON中的日期时间字符串
/// </summary>
/// <param name="dateString">日期时间字符串</param>
/// <returns>日期时间对象</returns>
private DateTime ParseJsonDateTime(string dateString)
{
if (string.IsNullOrEmpty(dateString))
{
return DateTime.Now;
}
if (DateTime.TryParseExact(dateString, "yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var result))
{
return result;
}
// 尝试其他常见格式
if (DateTime.TryParse(dateString, out result))
{
return result;
}
LogManager.Warning($"无法解析日期时间字符串: {dateString},使用当前时间");
return DateTime.Now;
}
/// <summary>
/// 保存路径到Navisworks文件
/// </summary>
/// <param name="routes">路径集合</param>
/// <param name="filePath">文件路径</param>
/// <returns>是否成功</returns>
public bool SaveToNavisworksFile(List<PathRoute> routes, string filePath)
{
try
{
// 创建临时属性来存储路径数据
var document = Application.ActiveDocument ?? throw new InvalidOperationException("没有活动的Navisworks文档");
// 简化实现:跳过路径数据序列化
// 这里可以将路径数据保存为自定义属性
// 由于Navisworks API的限制这里是简化实现
// 实际应用中可能需要使用COM API来添加自定义属性
// 保存当前文档
if (Path.GetExtension(filePath).ToLower() == ".nwd")
{
// 创建NWD导出选项和发布属性
var exportOptions = new NwdExportOptions();
var publishProperties = new PublishProperties();
document.PublishFile(filePath, exportOptions, publishProperties);
}
else
{
document.SaveFile(filePath);
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"保存到Navisworks文件失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 从Navisworks文件加载路径
/// </summary>
/// <param name="filePath">文件路径</param>
/// <returns>路径集合</returns>
public List<PathRoute> LoadFromNavisworksFile(string filePath)
{
var routes = new List<PathRoute>();
try
{
// 这里应该从Navisworks文件的自定义属性中读取路径数据
// 由于API限制这里是简化实现
// 实际应用中需要使用COM API来读取自定义属性
return routes;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"从Navisworks文件加载失败: {ex.Message}");
return routes;
}
}
/// <summary>
/// 导出DELMIA兼容格式
/// </summary>
/// <param name="routes">路径集合</param>
/// <param name="filePath">文件路径</param>
/// <param name="exportSettings">导出设置</param>
/// <returns>是否成功</returns>
public bool ExportDelmiaFormat(List<PathRoute> routes, string filePath, ExportSettings exportSettings = null)
{
try
{
// 确保导出目录存在
var directory = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
var xmlDoc = new XmlDocument();
var declaration = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null);
xmlDoc.AppendChild(declaration);
// 创建DELMIA格式的根元素
var rootElement = xmlDoc.CreateElement("DELMIA_PathPlanning", _delmiaNamespace);
rootElement.SetAttribute("version", _delmiaVersion);
rootElement.SetAttribute("units", exportSettings?.Units ?? "meters");
xmlDoc.AppendChild(rootElement);
// 添加任务信息
var taskInfo = xmlDoc.CreateElement("TaskInfo", _delmiaNamespace);
taskInfo.SetAttribute("name", exportSettings?.ProjectName ?? "物流路径规划");
taskInfo.SetAttribute("description", exportSettings?.Description ?? "从Navisworks导出的路径规划任务");
taskInfo.SetAttribute("created", DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss"));
rootElement.AppendChild(taskInfo);
// 添加路径集合
var pathsElement = xmlDoc.CreateElement("Paths", _delmiaNamespace);
rootElement.AppendChild(pathsElement);
foreach (var route in routes.Where(r => r.IsValid()))
{
var pathElement = CreateDelmiaPathElement(xmlDoc, route, exportSettings);
pathsElement.AppendChild(pathElement);
}
// 保存文件
using (var writer = new XmlTextWriter(filePath, Encoding.UTF8))
{
writer.Formatting = Formatting.Indented;
writer.Indentation = 2;
xmlDoc.WriteTo(writer);
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"导出DELMIA格式失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 导出路径到DELMIA Apriso XML格式符合TransportPathOrders Schema
/// </summary>
/// <param name="routes">路径集合</param>
/// <param name="filePath">文件路径</param>
/// <param name="exportSettings">导出设置</param>
/// <returns>是否成功</returns>
public bool ExportToDelmiaAprisoXml(List<PathRoute> routes, string filePath, ExportSettings exportSettings = null)
{
if (routes == null || routes.Count == 0)
{
throw new ArgumentException("路径集合不能为空");
}
try
{
LogManager.Info($"[路径文件操作] 开始导出 {routes.Count} 条路径到DELMIA Apriso XML文件: {filePath}");
// 确保导出目录存在
var directory = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
var xmlDoc = new XmlDocument();
var declaration = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null);
xmlDoc.AppendChild(declaration);
// DELMIA Apriso命名空间
const string ns = "http://www.dassaultsystemes.com/delmia/apriso";
const string nsPrefix = "apr";
// 创建根元素 TransportPathOrders
var rootElement = xmlDoc.CreateElement(nsPrefix, "TransportPathOrders", ns);
xmlDoc.AppendChild(rootElement);
// 为每个路径创建PathOrder
foreach (var route in routes.Where(r => r.IsValid()))
{
var pathOrderElement = CreateDelmiaAprisoPathOrderElement(xmlDoc, route, exportSettings, ns, nsPrefix);
rootElement.AppendChild(pathOrderElement);
}
// 保存文件
using (var writer = new XmlTextWriter(filePath, Encoding.UTF8))
{
writer.Formatting = Formatting.Indented;
writer.Indentation = 2;
xmlDoc.WriteTo(writer);
}
LogManager.Info($"[路径文件操作] 成功导出 {routes.Count} 条路径到DELMIA Apriso XML文件: {filePath}");
return true;
}
catch (Exception ex)
{
LogManager.Error($"[路径文件操作] 导出DELMIA Apriso XML文件失败: {ex.Message}", ex);
return false;
}
}
/// <summary>
/// 创建DELMIA Apriso格式的PathOrder元素
/// </summary>
private XmlElement CreateDelmiaAprisoPathOrderElement(XmlDocument xmlDoc, PathRoute route, ExportSettings settings, string ns, string nsPrefix)
{
var pathOrderElement = xmlDoc.CreateElement(nsPrefix, "PathOrder", ns);
// 基础信息
var orderIdElement = xmlDoc.CreateElement(nsPrefix, "OrderId", ns);
orderIdElement.InnerText = route.Id ?? Guid.NewGuid().ToString();
pathOrderElement.AppendChild(orderIdElement);
var orderNameElement = xmlDoc.CreateElement(nsPrefix, "OrderName", ns);
orderNameElement.InnerText = route.Name ?? "未命名路径";
pathOrderElement.AppendChild(orderNameElement);
var descriptionElement = xmlDoc.CreateElement(nsPrefix, "Description", ns);
descriptionElement.InnerText = route.Description ?? $"NavisworksTransport生成的路径: {route.Name}";
pathOrderElement.AppendChild(descriptionElement);
var pathTypeElement = xmlDoc.CreateElement(nsPrefix, "PathType", ns);
pathTypeElement.InnerText = MapToDelmiaPathType(route.PathType);
pathOrderElement.AppendChild(pathTypeElement);
var totalLengthElement = xmlDoc.CreateElement(nsPrefix, "TotalLength", ns);
totalLengthElement.InnerText = route.TotalLength.ToString("F3", CultureInfo.InvariantCulture);
pathOrderElement.AppendChild(totalLengthElement);
var timestampElement = xmlDoc.CreateElement(nsPrefix, "Timestamp", ns);
timestampElement.InnerText = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
pathOrderElement.AppendChild(timestampElement);
var generatorElement = xmlDoc.CreateElement(nsPrefix, "Generator", ns);
generatorElement.InnerText = "NavisworksTransport";
pathOrderElement.AppendChild(generatorElement);
var versionElement = xmlDoc.CreateElement(nsPrefix, "Version", ns);
versionElement.InnerText = "1.0";
pathOrderElement.AppendChild(versionElement);
// 创建Nodes
var nodesElement = xmlDoc.CreateElement(nsPrefix, "Nodes", ns);
pathOrderElement.AppendChild(nodesElement);
var sortedPoints = route.GetSortedPoints();
for (int i = 0; i < sortedPoints.Count; i++)
{
var point = sortedPoints[i];
var nodeElement = xmlDoc.CreateElement(nsPrefix, "Node", ns);
// NodeId
var nodeIdElement = xmlDoc.CreateElement(nsPrefix, "NodeId", ns);
nodeIdElement.InnerText = point.Id ?? $"node_{i}";
nodeElement.AppendChild(nodeIdElement);
// SequenceId
var sequenceIdElement = xmlDoc.CreateElement(nsPrefix, "SequenceId", ns);
sequenceIdElement.InnerText = i.ToString();
nodeElement.AppendChild(sequenceIdElement);
// NodeName
var nodeNameElement = xmlDoc.CreateElement(nsPrefix, "NodeName", ns);
nodeNameElement.InnerText = point.Name ?? $"点{i}";
nodeElement.AppendChild(nodeNameElement);
// NodeType (映射到DELMIA枚举支持吊装路径特殊类型)
var nodeTypeElement = xmlDoc.CreateElement(nsPrefix, "NodeType", ns);
string nodeType = MapToDelmiaNodeType(point.Type, route.PathType, i, sortedPoints.Count);
nodeTypeElement.InnerText = nodeType;
nodeElement.AppendChild(nodeTypeElement);
// 吊装路径特殊属性LiftHeight (仅对LiftPoint和HoverPoint)
if ((nodeType == "LiftPoint" || nodeType == "HoverPoint") && route.LiftHeight > 0)
{
var liftHeightElement = xmlDoc.CreateElement(nsPrefix, "LiftHeight", ns);
liftHeightElement.InnerText = route.LiftHeight.ToString("F3", CultureInfo.InvariantCulture);
nodeElement.AppendChild(liftHeightElement);
}
// Position
var positionElement = xmlDoc.CreateElement(nsPrefix, "Position", ns);
var xElement = xmlDoc.CreateElement(nsPrefix, "X", ns);
xElement.InnerText = point.Position.X.ToString("F3", CultureInfo.InvariantCulture);
positionElement.AppendChild(xElement);
var yElement = xmlDoc.CreateElement(nsPrefix, "Y", ns);
yElement.InnerText = point.Position.Y.ToString("F3", CultureInfo.InvariantCulture);
positionElement.AppendChild(yElement);
var zElement = xmlDoc.CreateElement(nsPrefix, "Z", ns);
zElement.InnerText = point.Position.Z.ToString("F3", CultureInfo.InvariantCulture);
positionElement.AppendChild(zElement);
nodeElement.AppendChild(positionElement);
// Released
var releasedElement = xmlDoc.CreateElement(nsPrefix, "Released", ns);
releasedElement.InnerText = "true";
nodeElement.AppendChild(releasedElement);
nodesElement.AppendChild(nodeElement);
}
// 创建Edges
var edgesElement = xmlDoc.CreateElement(nsPrefix, "Edges", ns);
pathOrderElement.AppendChild(edgesElement);
if (route.Edges != null && route.Edges.Count > 0)
{
for (int i = 0; i < route.Edges.Count && i < sortedPoints.Count - 1; i++)
{
var edge = route.Edges[i];
var startNode = sortedPoints[i];
var endNode = sortedPoints[i + 1];
var edgeElement = xmlDoc.CreateElement(nsPrefix, "Edge", ns);
// EdgeId
var edgeIdElement = xmlDoc.CreateElement(nsPrefix, "EdgeId", ns);
edgeIdElement.InnerText = $"edge_{i}";
edgeElement.AppendChild(edgeIdElement);
// EdgeType (支持吊装路径特殊类型)
var edgeTypeElement = xmlDoc.CreateElement(nsPrefix, "EdgeType", ns);
string edgeType = MapToDelmiaEdgeType(edge.SegmentType, route.PathType);
edgeTypeElement.InnerText = edgeType;
edgeElement.AppendChild(edgeTypeElement);
// SequenceId
var edgeSeqIdElement = xmlDoc.CreateElement(nsPrefix, "SequenceId", ns);
edgeSeqIdElement.InnerText = i.ToString();
edgeElement.AppendChild(edgeSeqIdElement);
// StartNodeId
var startNodeIdElement = xmlDoc.CreateElement(nsPrefix, "StartNodeId", ns);
startNodeIdElement.InnerText = startNode.Id ?? $"node_{i}";
edgeElement.AppendChild(startNodeIdElement);
// EndNodeId
var endNodeIdElement = xmlDoc.CreateElement(nsPrefix, "EndNodeId", ns);
endNodeIdElement.InnerText = endNode.Id ?? $"node_{i + 1}";
edgeElement.AppendChild(endNodeIdElement);
// PhysicalLength
var physicalLengthElement = xmlDoc.CreateElement(nsPrefix, "PhysicalLength", ns);
physicalLengthElement.InnerText = edge.PhysicalLength.ToString("F3", CultureInfo.InvariantCulture);
edgeElement.AppendChild(physicalLengthElement);
// Trajectory (如果是弧线)
if (edge.SegmentType == PathSegmentType.Arc && edge.Trajectory != null)
{
var trajectoryElement = xmlDoc.CreateElement(nsPrefix, "Trajectory", ns);
// StartTangent
var startTangentElement = xmlDoc.CreateElement(nsPrefix, "StartTangent", ns);
var stXElement = xmlDoc.CreateElement(nsPrefix, "X", ns);
stXElement.InnerText = edge.Trajectory.Ts.X.ToString("F3", CultureInfo.InvariantCulture);
startTangentElement.AppendChild(stXElement);
var stYElement = xmlDoc.CreateElement(nsPrefix, "Y", ns);
stYElement.InnerText = edge.Trajectory.Ts.Y.ToString("F3", CultureInfo.InvariantCulture);
startTangentElement.AppendChild(stYElement);
var stZElement = xmlDoc.CreateElement(nsPrefix, "Z", ns);
stZElement.InnerText = edge.Trajectory.Ts.Z.ToString("F3", CultureInfo.InvariantCulture);
startTangentElement.AppendChild(stZElement);
trajectoryElement.AppendChild(startTangentElement);
// EndTangent
var endTangentElement = xmlDoc.CreateElement(nsPrefix, "EndTangent", ns);
var etXElement = xmlDoc.CreateElement(nsPrefix, "X", ns);
etXElement.InnerText = edge.Trajectory.Te.X.ToString("F3", CultureInfo.InvariantCulture);
endTangentElement.AppendChild(etXElement);
var etYElement = xmlDoc.CreateElement(nsPrefix, "Y", ns);
etYElement.InnerText = edge.Trajectory.Te.Y.ToString("F3", CultureInfo.InvariantCulture);
endTangentElement.AppendChild(etYElement);
var etZElement = xmlDoc.CreateElement(nsPrefix, "Z", ns);
etZElement.InnerText = edge.Trajectory.Te.Z.ToString("F3", CultureInfo.InvariantCulture);
endTangentElement.AppendChild(etZElement);
trajectoryElement.AppendChild(endTangentElement);
// ArcCenter
var arcCenterElement = xmlDoc.CreateElement(nsPrefix, "ArcCenter", ns);
var acXElement = xmlDoc.CreateElement(nsPrefix, "X", ns);
acXElement.InnerText = edge.Trajectory.ArcCenter.X.ToString("F3", CultureInfo.InvariantCulture);
arcCenterElement.AppendChild(acXElement);
var acYElement = xmlDoc.CreateElement(nsPrefix, "Y", ns);
acYElement.InnerText = edge.Trajectory.ArcCenter.Y.ToString("F3", CultureInfo.InvariantCulture);
arcCenterElement.AppendChild(acYElement);
var acZElement = xmlDoc.CreateElement(nsPrefix, "Z", ns);
acZElement.InnerText = edge.Trajectory.ArcCenter.Z.ToString("F3", CultureInfo.InvariantCulture);
arcCenterElement.AppendChild(acZElement);
trajectoryElement.AppendChild(arcCenterElement);
// Radius
var radiusElement = xmlDoc.CreateElement(nsPrefix, "Radius", ns);
radiusElement.InnerText = edge.Trajectory.ActualRadius.ToString("F3", CultureInfo.InvariantCulture);
trajectoryElement.AppendChild(radiusElement);
// DeflectionAngle
var deflectionAngleElement = xmlDoc.CreateElement(nsPrefix, "DeflectionAngle", ns);
deflectionAngleElement.InnerText = edge.Trajectory.DeflectionAngle.ToString("F3", CultureInfo.InvariantCulture);
trajectoryElement.AppendChild(deflectionAngleElement);
edgeElement.AppendChild(trajectoryElement);
}
edgesElement.AppendChild(edgeElement);
}
}
// ObjectLimits (使用 -1 表示无限制)
var objectLimitsElement = xmlDoc.CreateElement(nsPrefix, "ObjectLimits", ns);
var maxLengthElement = xmlDoc.CreateElement(nsPrefix, "MaxLength", ns);
maxLengthElement.InnerText = (route.MaxObjectLength > 0 ? route.MaxObjectLength : -1).ToString("F3", CultureInfo.InvariantCulture);
objectLimitsElement.AppendChild(maxLengthElement);
var maxWidthElement = xmlDoc.CreateElement(nsPrefix, "MaxWidth", ns);
maxWidthElement.InnerText = (route.MaxObjectWidth > 0 ? route.MaxObjectWidth : -1).ToString("F3", CultureInfo.InvariantCulture);
objectLimitsElement.AppendChild(maxWidthElement);
var maxHeightElement = xmlDoc.CreateElement(nsPrefix, "MaxHeight", ns);
maxHeightElement.InnerText = (route.MaxObjectHeight > 0 ? route.MaxObjectHeight : -1).ToString("F3", CultureInfo.InvariantCulture);
objectLimitsElement.AppendChild(maxHeightElement);
var safetyMarginElement = xmlDoc.CreateElement(nsPrefix, "SafetyMargin", ns);
safetyMarginElement.InnerText = (route.SafetyMargin >= 0 ? route.SafetyMargin : 0.5).ToString("F3", CultureInfo.InvariantCulture);
objectLimitsElement.AppendChild(safetyMarginElement);
// MaxWeight (新增,默认 -1 表示无限制)
var maxWeightElement = xmlDoc.CreateElement(nsPrefix, "MaxWeight", ns);
maxWeightElement.InnerText = "-1";
objectLimitsElement.AppendChild(maxWeightElement);
pathOrderElement.AppendChild(objectLimitsElement);
// CoordinateSystem
var coordinateSystemElement = xmlDoc.CreateElement(nsPrefix, "CoordinateSystem", ns);
coordinateSystemElement.InnerText = settings?.CoordinateSystem ?? "Global";
pathOrderElement.AppendChild(coordinateSystemElement);
return pathOrderElement;
}
/// <summary>
/// 将PathPointType映射到DELMIA NodeType枚举
/// 支持吊装路径特殊节点类型LiftPoint, DescendPoint, HoverPoint
/// </summary>
private string MapToDelmiaNodeType(PathPointType type, PathType pathType = PathType.Ground, int pointIndex = 0, int totalPoints = 0)
{
// 基础类型映射
switch (type)
{
case PathPointType.StartPoint:
return "StartPoint";
case PathPointType.EndPoint:
return "EndPoint";
case PathPointType.WayPoint:
default:
// 对于吊装路径,根据点位置判断特殊类型
if (pathType == PathType.Hoisting && totalPoints > 2)
{
// 吊装路径的第二点通常是提升点
if (pointIndex == 1)
return "LiftPoint";
// 吊装路径的倒数第二点通常是下降点
if (pointIndex == totalPoints - 2)
return "DescendPoint";
// 中间空中点
if (pointIndex > 1 && pointIndex < totalPoints - 2)
return "HoverPoint";
}
return "WayPoint";
}
}
/// <summary>
/// 将PathType映射到DELMIA PathTypeEnum
/// </summary>
private string MapToDelmiaPathType(PathType pathType)
{
switch (pathType)
{
case PathType.Ground:
return "Ground";
case PathType.Rail:
case PathType.Hoisting:
return "Aerial";
default:
return "Mixed";
}
}
/// <summary>
/// 将PathSegmentType映射到DELMIA EdgeTypeEnum
/// 支持吊装路径特殊边类型vertical, suspension, hoisting
/// </summary>
private string MapToDelmiaEdgeType(PathSegmentType segmentType, PathType pathType = PathType.Ground)
{
switch (segmentType)
{
case PathSegmentType.Arc:
return "arc";
case PathSegmentType.Straight:
default:
// 对于吊装路径,根据上下文判断是否为垂直运动
if (pathType == PathType.Hoisting)
return "hoisting";
if (pathType == PathType.Rail)
return "suspension";
return "straight";
}
}
/// <summary>
/// 获取默认导出路径
/// </summary>
/// <param name="fileName">文件名</param>
/// <param name="format">格式</param>
/// <returns>完整路径</returns>
public string GetDefaultExportPath(string fileName, ExportFormat format)
{
var documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var exportPath = Path.Combine(documentsPath, DEFAULT_EXPORT_FOLDER);
if (!Directory.Exists(exportPath))
{
Directory.CreateDirectory(exportPath);
}
string extension;
switch (format)
{
case ExportFormat.Json:
extension = JSON_FILE_EXTENSION;
break;
case ExportFormat.Csv:
extension = CSV_FILE_EXTENSION;
break;
default:
extension = DEFAULT_FILE_EXTENSION;
break;
}
return Path.Combine(exportPath, fileName + extension);
}
/// <summary>
/// 验证导出文件
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="format">格式</param>
/// <returns>验证结果</returns>
public ValidationResult ValidateExportFile(string filePath, ExportFormat format)
{
var result = new ValidationResult();
try
{
if (!File.Exists(filePath))
{
result.IsValid = false;
result.Errors.Add("文件不存在");
return result;
}
if (format == ExportFormat.Xml)
{
// 验证XML格式
var xmlDoc = new XmlDocument();
xmlDoc.Load(filePath);
if (xmlDoc.DocumentElement?.Name != "PathPlanningData" &&
xmlDoc.DocumentElement?.Name != "DELMIA_PathPlanning")
{
result.Warnings.Add("XML根元素格式可能不正确");
}
}
else if (format == ExportFormat.Json)
{
// 验证JSON格式
try
{
var jsonContent = File.ReadAllText(filePath, Encoding.UTF8);
var serializer = new JavaScriptSerializer();
var parsed = serializer.DeserializeObject(jsonContent);
if (parsed == null)
{
result.Errors.Add("JSON文件格式无效");
}
else if (!jsonContent.Contains("PathPlanningData"))
{
result.Warnings.Add("JSON文件可能不包含路径规划数据结构");
}
}
catch (Exception jsonEx)
{
result.Errors.Add($"JSON格式验证失败: {jsonEx.Message}");
}
}
else if (format == ExportFormat.Csv)
{
// 验证CSV格式
try
{
var lines = File.ReadAllLines(filePath, Encoding.UTF8);
if (lines.Length == 0)
{
result.Errors.Add("CSV文件为空");
}
else if (!lines[0].Contains("路径名称") || !lines[0].Contains("X坐标"))
{
result.Warnings.Add("CSV文件头格式可能不正确");
}
else if (lines.Length < 2)
{
result.Warnings.Add("CSV文件只包含表头没有数据行");
}
}
catch (Exception csvEx)
{
result.Errors.Add($"CSV格式验证失败: {csvEx.Message}");
}
}
result.IsValid = true;
}
catch (Exception ex)
{
result.IsValid = false;
result.Errors.Add($"文件验证失败: {ex.Message}");
}
return result;
}
#region
/// <summary>
/// 格式化JSON字符串简单的美化
/// </summary>
private string FormatJsonString(string jsonString)
{
try
{
var indent = 0;
var formatted = new StringBuilder();
var inString = false;
var escape = false;
foreach (char c in jsonString)
{
if (!escape && c == '"')
inString = !inString;
if (!inString)
{
switch (c)
{
case '{':
case '[':
formatted.Append(c);
formatted.AppendLine();
formatted.Append(new string(' ', ++indent * 2));
break;
case '}':
case ']':
formatted.AppendLine();
formatted.Append(new string(' ', --indent * 2));
formatted.Append(c);
break;
case ',':
formatted.Append(c);
formatted.AppendLine();
formatted.Append(new string(' ', indent * 2));
break;
case ':':
formatted.Append(c);
formatted.Append(' ');
break;
default:
formatted.Append(c);
break;
}
}
else
{
formatted.Append(c);
}
escape = !escape && c == '\\';
}
return formatted.ToString();
}
catch
{
// 如果格式化失败,返回原始字符串
return jsonString;
}
}
/// <summary>
/// 转义CSV值处理逗号、引号和换行符
/// </summary>
private string EscapeCsvValue(string value)
{
if (string.IsNullOrEmpty(value))
return "\"\"";
// 如果包含逗号、引号或换行符,需要用引号包围并转义内部引号
if (value.Contains(",") || value.Contains("\"") || value.Contains("\n") || value.Contains("\r"))
{
value = value.Replace("\"", "\"\""); // 转义引号
return $"\"{value}\"";
}
return value;
}
/// <summary>
/// 创建项目信息元素
/// </summary>
private XmlElement CreateProjectInfoElement(XmlDocument xmlDoc, ExportSettings settings)
{
var projectInfo = xmlDoc.CreateElement("ProjectInfo", _delmiaNamespace);
projectInfo.SetAttribute("name", settings?.ProjectName ?? "NavisworksTransport路径规划");
projectInfo.SetAttribute("description", settings?.Description ?? "物流路径规划数据");
projectInfo.SetAttribute("units", settings?.Units ?? "meters");
projectInfo.SetAttribute("coordinateSystem", settings?.CoordinateSystem ?? "Global");
return projectInfo;
}
/// <summary>
/// 创建路径元素
/// </summary>
private XmlElement CreateRouteElement(XmlDocument xmlDoc, PathRoute route, ExportSettings settings)
{
var routeElement = xmlDoc.CreateElement("Route", _delmiaNamespace);
routeElement.SetAttribute("id", route.Id);
routeElement.SetAttribute("name", route.Name);
routeElement.SetAttribute("description", route.Description ?? "");
routeElement.SetAttribute("pathType", route.PathType.ToString());
routeElement.SetAttribute("railMountMode", route.RailMountMode.ToString());
routeElement.SetAttribute("railPathDefinitionMode", route.RailPathDefinitionMode.ToString());
routeElement.SetAttribute("railNormalOffset", route.RailNormalOffset.ToString("F3"));
if (route.RailPreferredNormal != null)
{
routeElement.SetAttribute("railPreferredNormalX", route.RailPreferredNormal.X.ToString("F3"));
routeElement.SetAttribute("railPreferredNormalY", route.RailPreferredNormal.Y.ToString("F3"));
routeElement.SetAttribute("railPreferredNormalZ", route.RailPreferredNormal.Z.ToString("F3"));
}
routeElement.SetAttribute("totalLength", route.TotalLength.ToString("F3"));
routeElement.SetAttribute("maxObjectLength", route.MaxObjectLength.ToString("F3"));
routeElement.SetAttribute("maxObjectWidth", route.MaxObjectWidth.ToString("F3"));
routeElement.SetAttribute("maxObjectHeight", route.MaxObjectHeight.ToString("F3"));
routeElement.SetAttribute("safetyMargin", route.SafetyMargin.ToString("F3"));
routeElement.SetAttribute("gridSize", route.GridSize.ToString("F3"));
routeElement.SetAttribute("liftHeight", route.LiftHeight.ToString("F3"));
routeElement.SetAttribute("created", route.CreatedTime.ToString("yyyy-MM-ddTHH:mm:ss"));
// 添加路径点
var pointsElement = xmlDoc.CreateElement("Points", _delmiaNamespace);
routeElement.AppendChild(pointsElement);
var sortedPoints = route.GetSortedPoints();
foreach (var point in sortedPoints)
{
var pointElement = xmlDoc.CreateElement("Point", _delmiaNamespace);
pointElement.SetAttribute("id", point.Id);
pointElement.SetAttribute("name", point.Name);
pointElement.SetAttribute("type", point.Type.ToString());
pointElement.SetAttribute("index", point.Index.ToString());
pointElement.SetAttribute("x", point.Position.X.ToString("F3"));
pointElement.SetAttribute("y", point.Position.Y.ToString("F3"));
pointElement.SetAttribute("z", point.Position.Z.ToString("F3"));
pointElement.SetAttribute("created", point.CreatedTime.ToString("yyyy-MM-ddTHH:mm:ss"));
pointsElement.AppendChild(pointElement);
}
// 添加路径边
if (route.Edges != null && route.Edges.Count > 0)
{
var edgesElement = xmlDoc.CreateElement("Edges", _delmiaNamespace);
routeElement.AppendChild(edgesElement);
foreach (var edge in route.Edges)
{
var edgeElement = xmlDoc.CreateElement("Edge", _delmiaNamespace);
edgeElement.SetAttribute("type", edge.SegmentType.ToString().ToLower());
edgeElement.SetAttribute("physicalLength", edge.PhysicalLength.ToString("F3"));
// 圆弧轨迹数据
if (edge.SegmentType == PathSegmentType.Arc && edge.Trajectory != null)
{
var trajElement = xmlDoc.CreateElement("Trajectory", _delmiaNamespace);
// 进入切点
var tsElement = xmlDoc.CreateElement("Ts", _delmiaNamespace);
tsElement.SetAttribute("x", edge.Trajectory.Ts.X.ToString("F3"));
tsElement.SetAttribute("y", edge.Trajectory.Ts.Y.ToString("F3"));
tsElement.SetAttribute("z", edge.Trajectory.Ts.Z.ToString("F3"));
trajElement.AppendChild(tsElement);
// 退出切点
var teElement = xmlDoc.CreateElement("Te", _delmiaNamespace);
teElement.SetAttribute("x", edge.Trajectory.Te.X.ToString("F3"));
teElement.SetAttribute("y", edge.Trajectory.Te.Y.ToString("F3"));
teElement.SetAttribute("z", edge.Trajectory.Te.Z.ToString("F3"));
trajElement.AppendChild(teElement);
// 圆心
var centerElement = xmlDoc.CreateElement("ArcCenter", _delmiaNamespace);
centerElement.SetAttribute("x", edge.Trajectory.ArcCenter.X.ToString("F3"));
centerElement.SetAttribute("y", edge.Trajectory.ArcCenter.Y.ToString("F3"));
centerElement.SetAttribute("z", edge.Trajectory.ArcCenter.Z.ToString("F3"));
trajElement.AppendChild(centerElement);
// 请求半径
var requestedRadiusElement = xmlDoc.CreateElement("RequestedRadius", _delmiaNamespace);
requestedRadiusElement.InnerText = edge.Trajectory.RequestedRadius.ToString("F3");
trajElement.AppendChild(requestedRadiusElement);
// 实际半径
var radiusElement = xmlDoc.CreateElement("ActualRadius", _delmiaNamespace);
radiusElement.InnerText = edge.Trajectory.ActualRadius.ToString("F3");
trajElement.AppendChild(radiusElement);
// 偏转角
var angleElement = xmlDoc.CreateElement("DeflectionAngle", _delmiaNamespace);
angleElement.InnerText = edge.Trajectory.DeflectionAngle.ToString("F3");
trajElement.AppendChild(angleElement);
// 圆弧长度
var arcLengthElement = xmlDoc.CreateElement("ArcLength", _delmiaNamespace);
arcLengthElement.InnerText = edge.Trajectory.ArcLength.ToString("F3");
trajElement.AppendChild(arcLengthElement);
edgeElement.AppendChild(trajElement);
}
edgesElement.AppendChild(edgeElement);
}
}
return routeElement;
}
/// <summary>
/// 解析路径元素
/// </summary>
private PathRoute ParseRouteElement(XmlNode routeNode)
{
try
{
// 🔥 路径类型是必需字段
var pathTypeStr = routeNode.Attributes?["pathType"]?.Value;
if (string.IsNullOrEmpty(pathTypeStr))
{
LogManager.Error("XML导入失败路径类型(pathType)属性缺失");
return null;
}
if (!Enum.TryParse<PathType>(pathTypeStr, out var pathType))
{
LogManager.Error($"XML导入失败无法解析路径类型 '{pathTypeStr}'");
return null;
}
var route = new PathRoute
{
Id = routeNode.Attributes?["id"]?.Value ?? Guid.NewGuid().ToString(),
Name = routeNode.Attributes?["name"]?.Value ?? "导入路径",
Description = routeNode.Attributes?["description"]?.Value ?? "",
PathType = pathType,
MaxObjectLength = double.TryParse(routeNode.Attributes?["maxObjectLength"]?.Value, out double maxLength) ? maxLength : 0,
MaxObjectWidth = double.TryParse(routeNode.Attributes?["maxObjectWidth"]?.Value, out double maxWidth) ? maxWidth : 0,
MaxObjectHeight = double.TryParse(routeNode.Attributes?["maxObjectHeight"]?.Value, out double maxHeight) ? maxHeight : 0,
SafetyMargin = double.TryParse(routeNode.Attributes?["safetyMargin"]?.Value, out double safetyMargin) ? safetyMargin : 0,
GridSize = double.TryParse(routeNode.Attributes?["gridSize"]?.Value, out double gridSize) ? gridSize : 0,
LiftHeight = double.TryParse(routeNode.Attributes?["liftHeight"]?.Value, out double liftHeight) ? liftHeight : 0
};
if (Enum.TryParse<RailMountMode>(routeNode.Attributes?["railMountMode"]?.Value, out var railMountMode))
{
route.RailMountMode = railMountMode;
}
if (Enum.TryParse<RailPathDefinitionMode>(routeNode.Attributes?["railPathDefinitionMode"]?.Value, out var railPathDefinitionMode))
{
route.RailPathDefinitionMode = railPathDefinitionMode;
}
if (double.TryParse(routeNode.Attributes?["railNormalOffset"]?.Value, out var railNormalOffset))
{
route.RailNormalOffset = railNormalOffset;
}
if (double.TryParse(routeNode.Attributes?["railPreferredNormalX"]?.Value, out var railPreferredNormalX) &&
double.TryParse(routeNode.Attributes?["railPreferredNormalY"]?.Value, out var railPreferredNormalY) &&
double.TryParse(routeNode.Attributes?["railPreferredNormalZ"]?.Value, out var railPreferredNormalZ))
{
route.RailPreferredNormal = new Point3D(
railPreferredNormalX,
railPreferredNormalY,
railPreferredNormalZ);
}
// 解析创建时间
if (DateTime.TryParse(routeNode.Attributes?["created"]?.Value, out DateTime createdTime))
{
route.CreatedTime = createdTime;
}
// 解析路径点
var pointsNode = routeNode.SelectSingleNode("*[local-name()='Points']");
if (pointsNode != null)
{
foreach (XmlNode pointNode in pointsNode.ChildNodes)
{
if (pointNode.NodeType == XmlNodeType.Element)
{
var point = ParsePointElement(pointNode);
if (point != null)
{
route.AddPoint(point);
}
}
}
}
return route;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"解析路径元素失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 解析路径点元素
/// </summary>
private PathPoint ParsePointElement(XmlNode pointNode)
{
try
{
var point = new PathPoint
{
Id = pointNode.Attributes?["id"]?.Value ?? Guid.NewGuid().ToString(),
Name = pointNode.Attributes?["name"]?.Value ?? ""
};
// 解析位置
if (double.TryParse(pointNode.Attributes?["x"]?.Value, out double x) &&
double.TryParse(pointNode.Attributes?["y"]?.Value, out double y) &&
double.TryParse(pointNode.Attributes?["z"]?.Value, out double z))
{
point.Position = new Point3D(x, y, z);
}
// 解析索引
if (int.TryParse(pointNode.Attributes?["index"]?.Value, out int index))
{
point.Index = index;
}
// 解析类型
if (Enum.TryParse<PathPointType>(pointNode.Attributes?["type"]?.Value, out PathPointType type))
{
point.Type = type;
}
// 解析创建时间
if (DateTime.TryParse(pointNode.Attributes?["created"]?.Value, out DateTime createdTime))
{
point.CreatedTime = createdTime;
}
return point;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"解析路径点元素失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 创建DELMIA路径元素
/// </summary>
private XmlElement CreateDelmiaPathElement(XmlDocument xmlDoc, PathRoute route, ExportSettings settings)
{
var pathElement = xmlDoc.CreateElement("Path", _delmiaNamespace);
pathElement.SetAttribute("id", route.Id);
pathElement.SetAttribute("name", route.Name);
pathElement.SetAttribute("type", "Transportation");
pathElement.SetAttribute("length", route.TotalLength.ToString("F3"));
// 添加物体限制元素
var objectConstraints = xmlDoc.CreateElement("ObjectConstraints", _delmiaNamespace);
objectConstraints.SetAttribute("maxLength", route.MaxObjectLength.ToString("F3"));
objectConstraints.SetAttribute("maxWidth", route.MaxObjectWidth.ToString("F3"));
objectConstraints.SetAttribute("maxHeight", route.MaxObjectHeight.ToString("F3"));
objectConstraints.SetAttribute("safetyMargin", route.SafetyMargin.ToString("F3"));
objectConstraints.SetAttribute("gridSize", route.GridSize.ToString("F3"));
pathElement.AppendChild(objectConstraints);
// 添加路径段
var segmentsElement = xmlDoc.CreateElement("Segments", _delmiaNamespace);
pathElement.AppendChild(segmentsElement);
var sortedPoints = route.GetSortedPoints();
for (int i = 0; i < sortedPoints.Count - 1; i++)
{
var segmentElement = xmlDoc.CreateElement("Segment", _delmiaNamespace);
segmentElement.SetAttribute("index", i.ToString());
// 起点
var startElement = xmlDoc.CreateElement("Start", _delmiaNamespace);
AddDelmiaPointData(xmlDoc, startElement, sortedPoints[i]);
segmentElement.AppendChild(startElement);
// 终点
var endElement = xmlDoc.CreateElement("End", _delmiaNamespace);
AddDelmiaPointData(xmlDoc, endElement, sortedPoints[i + 1]);
segmentElement.AppendChild(endElement);
segmentsElement.AppendChild(segmentElement);
}
return pathElement;
}
/// <summary>
/// 添加DELMIA点数据
/// </summary>
private void AddDelmiaPointData(XmlDocument xmlDoc, XmlElement element, PathPoint point)
{
element.SetAttribute("x", point.Position.X.ToString("F3"));
element.SetAttribute("y", point.Position.Y.ToString("F3"));
element.SetAttribute("z", point.Position.Z.ToString("F3"));
element.SetAttribute("name", point.Name);
element.SetAttribute("type", point.Type.ToString());
}
#endregion
}
/// <summary>
/// 导出格式枚举
/// </summary>
public enum ExportFormat
{
Xml,
Json,
Csv,
Delmia,
Navisworks,
DelmiaApriso
}
/// <summary>
/// 导出设置
/// </summary>
public class ExportSettings
{
/// <summary>
/// 项目名称
/// </summary>
public string ProjectName { get; set; } = "NavisworksTransport路径规划";
/// <summary>
/// 项目描述
/// </summary>
public string Description { get; set; } = "物流路径规划数据";
/// <summary>
/// 单位
/// </summary>
public string Units { get; set; } = "meters";
/// <summary>
/// 坐标系
/// </summary>
public string CoordinateSystem { get; set; } = "Global";
/// <summary>
/// 是否包含时间戳
/// </summary>
public bool IncludeTimestamps { get; set; } = true;
/// <summary>
/// 是否包含元数据
/// </summary>
public bool IncludeMetadata { get; set; } = true;
/// <summary>
/// 精度(小数位数)
/// </summary>
public int Precision { get; set; } = 3;
}
/// <summary>
/// 验证结果
/// </summary>
public class ValidationResult
{
/// <summary>
/// 是否有效
/// </summary>
public bool IsValid { get; set; } = true;
/// <summary>
/// 错误信息
/// </summary>
public List<string> Errors { get; set; } = new List<string>();
/// <summary>
/// 警告信息
/// </summary>
public List<string> Warnings { get; set; } = new List<string>();
/// <summary>
/// 获取所有消息
/// </summary>
/// <returns>消息集合</returns>
public IEnumerable<string> GetAllMessages()
{
return Errors.Concat(Warnings);
}
}
}