diff --git a/doc/design/2026/转DelmiaXML的步骤.md b/doc/design/2026/转DelmiaXML的步骤.md
new file mode 100644
index 0000000..44b5102
--- /dev/null
+++ b/doc/design/2026/转DelmiaXML的步骤.md
@@ -0,0 +1,650 @@
+# 转 DELMIA Apriso XML 的步骤
+
+直接生成符合 DELMIA Apriso 要求的 TransportPathOrders XML,跳过 XSLT 转换步骤。
+简化方案:直接生成目标XML
+目标Schema(直接使用)
+创建文件 TransportPathOrder.xsd(这是唯一需要的Schema文件):
+xml
+复制
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+您的程序直接生成XML的代码
+C# 示例(如果您用C#)
+csharp
+复制
+using System;
+using System.Collections.Generic;
+using System.Xml.Linq;
+using System.IO;
+
+public class TransportPathGenerator
+{
+ public class PathPoint
+ {
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string Type { get; set; } // StartPoint, WayPoint, EndPoint
+ public int Index { get; set; }
+ public double X { get; set; }
+ public double Y { get; set; }
+ public double Z { get; set; }
+ }
+
+ public class PathEdge
+ {
+ public string Type { get; set; } // straight, arc
+ public double PhysicalLength { get; set; }
+ public TrajectoryInfo Trajectory { get; set; }
+ }
+
+ public class TrajectoryInfo
+ {
+ public Point3D StartTangent { get; set; }
+ public Point3D EndTangent { get; set; }
+ public Point3D ArcCenter { get; set; }
+ public double Radius { get; set; }
+ public double DeflectionAngle { get; set; }
+ }
+
+ public class Point3D
+ {
+ public double X { get; set; }
+ public double Y { get; set; }
+ public double Z { get; set; }
+ }
+
+ public void GenerateXml(
+ string orderId,
+ string orderName,
+ List points,
+ List edges,
+ string outputPath)
+ {
+ XNamespace ns = "http://www.dassaultsystemes.com/delmia/apriso";
+
+ // 构建Nodes
+ var nodesElement = new XElement(ns + "Nodes");
+ foreach (var point in points)
+ {
+ nodesElement.Add(new XElement(ns + "Node",
+ new XElement(ns + "NodeId", point.Id),
+ new XElement(ns + "SequenceId", point.Index),
+ new XElement(ns + "NodeName", point.Name),
+ new XElement(ns + "NodeType", point.Type),
+ new XElement(ns + "Position",
+ new XElement(ns + "X", point.X),
+ new XElement(ns + "Y", point.Y),
+ new XElement(ns + "Z", point.Z)
+ ),
+ new XElement(ns + "Released", "true")
+ ));
+ }
+
+ // 构建Edges(需要关联起点和终点ID)
+ var edgesElement = new XElement(ns + "Edges");
+ for (int i = 0; i < edges.Count; i++)
+ {
+ var edge = edges[i];
+ var startNode = points[i];
+ var endNode = points[i + 1];
+
+ var edgeElement = new XElement(ns + "Edge",
+ new XElement(ns + "EdgeId", $"edge_{i}"),
+ new XElement(ns + "EdgeType", edge.Type),
+ new XElement(ns + "SequenceId", i),
+ new XElement(ns + "StartNodeId", startNode.Id),
+ new XElement(ns + "EndNodeId", endNode.Id),
+ new XElement(ns + "PhysicalLength", edge.PhysicalLength)
+ );
+
+ // 如果是弧线,添加轨迹信息
+ if (edge.Type == "arc" && edge.Trajectory != null)
+ {
+ edgeElement.Add(new XElement(ns + "Trajectory",
+ new XElement(ns + "StartTangent",
+ new XElement(ns + "X", edge.Trajectory.StartTangent.X),
+ new XElement(ns + "Y", edge.Trajectory.StartTangent.Y),
+ new XElement(ns + "Z", edge.Trajectory.StartTangent.Z)
+ ),
+ new XElement(ns + "EndTangent",
+ new XElement(ns + "X", edge.Trajectory.EndTangent.X),
+ new XElement(ns + "Y", edge.Trajectory.EndTangent.Y),
+ new XElement(ns + "Z", edge.Trajectory.EndTangent.Z)
+ ),
+ new XElement(ns + "ArcCenter",
+ new XElement(ns + "X", edge.Trajectory.ArcCenter.X),
+ new XElement(ns + "Y", edge.Trajectory.ArcCenter.Y),
+ new XElement(ns + "Z", edge.Trajectory.ArcCenter.Z)
+ ),
+ new XElement(ns + "Radius", edge.Trajectory.Radius),
+ new XElement(ns + "DeflectionAngle", edge.Trajectory.DeflectionAngle)
+ ));
+ }
+
+ edgesElement.Add(edgeElement);
+ }
+
+ // 构建完整文档
+ var document = new XDocument(
+ new XDeclaration("1.0", "utf-8", null),
+ new XElement(ns + "TransportPathOrders",
+ new XElement(ns + "PathOrder",
+ new XElement(ns + "OrderId", orderId),
+ new XElement(ns + "OrderName", orderName),
+ new XElement(ns + "Description", "NavisworksTransport生成的路径"),
+ new XElement(ns + "PathType", "Ground"),
+ new XElement(ns + "TotalLength", CalculateTotalLength(edges)),
+ new XElement(ns + "Timestamp", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")),
+ new XElement(ns + "Generator", "NavisworksTransport"),
+ new XElement(ns + "Version", "1.0"),
+ nodesElement,
+ edgesElement,
+ new XElement(ns + "ObjectLimits",
+ new XElement(ns + "MaxLength", "0"),
+ new XElement(ns + "MaxWidth", "0"),
+ new XElement(ns + "MaxHeight", "0"),
+ new XElement(ns + "SafetyMargin", "0")
+ ),
+ new XElement(ns + "CoordinateSystem", "Global")
+ )
+ )
+ );
+
+ document.Save(outputPath);
+ }
+
+ private double CalculateTotalLength(List edges)
+ {
+ double total = 0;
+ foreach (var edge in edges)
+ total += edge.PhysicalLength;
+ return total;
+ }
+}
+
+// 使用示例
+class Program
+{
+ static void Main()
+ {
+ var generator = new TransportPathGenerator();
+
+ var points = new List
+ {
+ new AGVPathGenerator.PathPoint
+ {
+ Id = "3ada559e-36b9-4537-b341-cfff765c8d43",
+ Name = "起点",
+ Type = "StartPoint",
+ Index = 0,
+ X = -152.614,
+ Y = 0.517,
+ Z = 14.833
+ },
+ new AGVPathGenerator.PathPoint
+ {
+ Id = "042229ba-0ce2-4f6c-af8f-57f840300e82",
+ Name = "路径点1",
+ Type = "WayPoint",
+ Index = 1,
+ X = -131.345,
+ Y = -14.532,
+ Z = 14.75
+ },
+ new AGVPathGenerator.PathPoint
+ {
+ Id = "69fa7321-3dc5-4ec5-9c1f-74e2a6d787a5",
+ Name = "终点",
+ Type = "EndPoint",
+ Index = 2,
+ X = -55.369,
+ Y = -0.504,
+ Z = 14.833
+ }
+ };
+
+ var edges = new List
+ {
+ new AGVPathGenerator.PathEdge
+ {
+ Type = "straight",
+ PhysicalLength = 22.595
+ },
+ new AGVPathGenerator.PathEdge
+ {
+ Type = "arc",
+ PhysicalLength = 6.548,
+ Trajectory = new AGVPathGenerator.TrajectoryInfo
+ {
+ StartTangent = new AGVPathGenerator.Point3D
+ { X = -134.17, Y = -12.533, Z = 14.761 },
+ EndTangent = new AGVPathGenerator.Point3D
+ { X = -127.943, Y = -13.903, Z = 14.754 },
+ ArcCenter = new AGVPathGenerator.Point3D
+ { X = -129.432, Y = -5.837, Z = 14.799 },
+ Radius = 8.202,
+ DeflectionAngle = 0.798
+ }
+ },
+ new AGVPathGenerator.PathEdge
+ {
+ Type = "straight",
+ PhysicalLength = 73.8
+ }
+ };
+
+ generator.GenerateXml(
+ "17838e6e-1112-4e62-a3ab-d7a3354d39d3",
+ "人工_0219_203325",
+ points,
+ edges,
+ "output.xml"
+ );
+ }
+}
+Python 示例(如果您用Python)
+Python
+复制
+import xml.etree.ElementTree as ET
+from datetime import datetime
+from dataclasses import dataclass
+from typing import List, Optional
+
+@dataclass
+class Point3D:
+ x: float
+ y: float
+ z: float
+
+@dataclass
+class TrajectoryInfo:
+ start_tangent: Point3D
+ end_tangent: Point3D
+ arc_center: Point3D
+ radius: float
+ deflection_angle: float
+
+@dataclass
+class PathPoint:
+ id: str
+ name: str
+ type: str # StartPoint, WayPoint, EndPoint
+ index: int
+ x: float
+ y: float
+ z: float
+
+@dataclass
+class PathEdge:
+ edge_type: str # straight, arc
+ physical_length: float
+ trajectory: Optional[TrajectoryInfo] = None
+
+class TransportPathGenerator:
+ def __init__(self):
+ self.ns = "http://www.dassaultsystemes.com/delmia/apriso"
+ ET.register_namespace('', self.ns)
+
+ def generate_xml(self,
+ order_id: str,
+ order_name: str,
+ points: List[PathPoint],
+ edges: List[PathEdge],
+ output_path: str):
+
+ # 创建根元素
+ root = ET.Element(f"{{{self.ns}}}TransportPathOrders")
+
+ # 创建PathOrder
+ path_order = ET.SubElement(root, f"{{{self.ns}}}PathOrder")
+
+ # 基础信息
+ ET.SubElement(path_order, f"{{{self.ns}}}OrderId").text = order_id
+ ET.SubElement(path_order, f"{{{self.ns}}}OrderName").text = order_name
+ ET.SubElement(path_order, f"{{{self.ns}}}Description").text = "NavisworksTransport生成的路径"
+ ET.SubElement(path_order, f"{{{self.ns}}}PathType").text = "Ground"
+ ET.SubElement(path_order, f"{{{self.ns}}}TotalLength").text = str(sum(e.physical_length for e in edges))
+ ET.SubElement(path_order, f"{{{self.ns}}}Timestamp").text = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
+ ET.SubElement(path_order, f"{{{self.ns}}}Generator").text = "NavisworksTransport"
+ ET.SubElement(path_order, f"{{{self.ns}}}Version").text = "1.0"
+
+ # 创建Nodes
+ nodes = ET.SubElement(path_order, f"{{{self.ns}}}Nodes")
+ for point in points:
+ node = ET.SubElement(nodes, f"{{{self.ns}}}Node")
+ ET.SubElement(node, f"{{{self.ns}}}NodeId").text = point.id
+ ET.SubElement(node, f"{{{self.ns}}}SequenceId").text = str(point.index)
+ ET.SubElement(node, f"{{{self.ns}}}NodeName").text = point.name
+ ET.SubElement(node, f"{{{self.ns}}}NodeType").text = point.type
+
+ position = ET.SubElement(node, f"{{{self.ns}}}Position")
+ ET.SubElement(position, f"{{{self.ns}}}X").text = str(point.x)
+ ET.SubElement(position, f"{{{self.ns}}}Y").text = str(point.y)
+ ET.SubElement(position, f"{{{self.ns}}}Z").text = str(point.z)
+
+ ET.SubElement(node, f"{{{self.ns}}}Released").text = "true"
+
+ # 创建Edges
+ edges_elem = ET.SubElement(path_order, f"{{{self.ns}}}Edges")
+ for i, edge in enumerate(edges):
+ edge_elem = ET.SubElement(edges_elem, f"{{{self.ns}}}Edge")
+ ET.SubElement(edge_elem, f"{{{self.ns}}}EdgeId").text = f"edge_{i}"
+ ET.SubElement(edge_elem, f"{{{self.ns}}}EdgeType").text = edge.edge_type
+ ET.SubElement(edge_elem, f"{{{self.ns}}}SequenceId").text = str(i)
+ ET.SubElement(edge_elem, f"{{{self.ns}}}StartNodeId").text = points[i].id
+ ET.SubElement(edge_elem, f"{{{self.ns}}}EndNodeId").text = points[i+1].id
+ ET.SubElement(edge_elem, f"{{{self.ns}}}PhysicalLength").text = str(edge.physical_length)
+
+ # 如果是弧线,添加轨迹
+ if edge.edge_type == "arc" and edge.trajectory:
+ traj = ET.SubElement(edge_elem, f"{{{self.ns}}}Trajectory")
+
+ start_tan = ET.SubElement(traj, f"{{{self.ns}}}StartTangent")
+ ET.SubElement(start_tan, f"{{{self.ns}}}X").text = str(edge.trajectory.start_tangent.x)
+ ET.SubElement(start_tan, f"{{{self.ns}}}Y").text = str(edge.trajectory.start_tangent.y)
+ ET.SubElement(start_tan, f"{{{self.ns}}}Z").text = str(edge.trajectory.start_tangent.z)
+
+ end_tan = ET.SubElement(traj, f"{{{self.ns}}}EndTangent")
+ ET.SubElement(end_tan, f"{{{self.ns}}}X").text = str(edge.trajectory.end_tangent.x)
+ ET.SubElement(end_tan, f"{{{self.ns}}}Y").text = str(edge.trajectory.end_tangent.y)
+ ET.SubElement(end_tan, f"{{{self.ns}}}Z").text = str(edge.trajectory.end_tangent.z)
+
+ arc_center = ET.SubElement(traj, f"{{{self.ns}}}ArcCenter")
+ ET.SubElement(arc_center, f"{{{self.ns}}}X").text = str(edge.trajectory.arc_center.x)
+ ET.SubElement(arc_center, f"{{{self.ns}}}Y").text = str(edge.trajectory.arc_center.y)
+ ET.SubElement(arc_center, f"{{{self.ns}}}Z").text = str(edge.trajectory.arc_center.z)
+
+ ET.SubElement(traj, f"{{{self.ns}}}Radius").text = str(edge.trajectory.radius)
+ ET.SubElement(traj, f"{{{self.ns}}}DeflectionAngle").text = str(edge.trajectory.deflection_angle)
+
+ # 对象限制
+ limits = ET.SubElement(path_order, f"{{{self.ns}}}ObjectLimits")
+ ET.SubElement(limits, f"{{{self.ns}}}MaxLength").text = "0"
+ ET.SubElement(limits, f"{{{self.ns}}}MaxWidth").text = "0"
+ ET.SubElement(limits, f"{{{self.ns}}}MaxHeight").text = "0"
+ ET.SubElement(limits, f"{{{self.ns}}}SafetyMargin").text = "0"
+
+ ET.SubElement(path_order, f"{{{self.ns}}}CoordinateSystem").text = "Global"
+
+ # 保存文件
+ tree = ET.ElementTree(root)
+ tree.write(output_path, encoding='utf-8', xml_declaration=True)
+ print(f"XML已生成: {output_path}")
+
+# 使用示例
+if __name__ == "__main__":
+ generator = TransportPathGenerator()
+
+ points = [
+ PathPoint("3ada559e-36b9-4537-b341-cfff765c8d43", "起点", "StartPoint", 0, -152.614, 0.517, 14.833),
+ PathPoint("042229ba-0ce2-4f6c-af8f-57f840300e82", "路径点1", "WayPoint", 1, -131.345, -14.532, 14.75),
+ PathPoint("69fa7321-3dc5-4ec5-9c1f-74e2a6d787a5", "终点", "EndPoint", 2, -55.369, -0.504, 14.833)
+ ]
+
+ edges = [
+ PathEdge("straight", 22.595),
+ PathEdge("arc", 6.548, TrajectoryInfo(
+ Point3D(-134.17, -12.533, 14.761),
+ Point3D(-127.943, -13.903, 14.754),
+ Point3D(-129.432, -5.837, 14.799),
+ 8.202, 0.798
+ )),
+ PathEdge("straight", 73.8)
+ ]
+
+ generator.generate_xml(
+ "17838e6e-1112-4e62-a3ab-d7a3354d39d3",
+ "人工_0219_203325",
+ points,
+ edges,
+ "transport_path.xml"
+ )
+生成的 XML 示例
+xml
+复制
+
+
+
+ 17838e6e-1112-4e62-a3ab-d7a3354d39d3
+ 人工_0219_203325
+ NavisworksTransport生成的路径
+ Ground
+ 102.943
+ 2026-03-09T16:30:45Z
+ NavisworksTransport
+ 1.0
+
+
+ 3ada559e-36b9-4537-b341-cfff765c8d43
+ 0
+ 起点
+ StartPoint
+
+ -152.614
+ 0.517
+ 14.833
+
+ true
+
+
+ 042229ba-0ce2-4f6c-af8f-57f840300e82
+ 1
+ 路径点1
+ WayPoint
+
+ -131.345
+ -14.532
+ 14.75
+
+ true
+
+
+ 69fa7321-3dc5-4ec5-9c1f-74e2a6d787a5
+ 2
+ 终点
+ EndPoint
+
+ -55.369
+ -0.504
+ 14.833
+
+ true
+
+
+
+
+ edge_0
+ straight
+ 0
+ 3ada559e-36b9-4537-b341-cfff765c8d43
+ 042229ba-0ce2-4f6c-af8f-57f840300e82
+ 22.595
+
+
+ edge_1
+ arc
+ 1
+ 042229ba-0ce2-4f6c-af8f-57f840300e82
+ 69fa7321-3dc5-4ec5-9c1f-74e2a6d787a5
+ 6.548
+
+
+ -134.17
+ -12.533
+ 14.761
+
+
+ -127.943
+ -13.903
+ 14.754
+
+
+ -129.432
+ -5.837
+ 14.799
+
+ 8.202
+ 0.798
+
+
+
+ edge_2
+ straight
+ 2
+ 69fa7321-3dc5-4ec5-9c1f-74e2a6d787a5
+ 终点_D
+ 73.8
+
+
+
+ 0
+ 0
+ 0
+ 0
+
+ Global
+
+
+DELMIA Apriso 配置(简化版)
+既然您直接生成目标XML,配置也简化了:
+1. 部署Schema
+将 TransportPathOrder.xsd 复制到:
+plain
+复制
+SchemaRepository/XMLSchemas/FlexNet/TransportPathOrder.xsd
+2. Routing Configuration(仅需路由,无需XSLT)
+xml
+复制
+
+
+
+注意:不需要 Mapping Configuration,因为您直接生成了 Apriso 内部格式!
+3. 创建Job Action处理程序
+在DELMIA Apriso中创建 ProcessTransportPathOrder 来处理导入的路径数据。
+关键优势
+✅ 无需 XSLT 转换:直接生成目标格式,性能更好
+✅ 配置更简单:只需配置路由,无需映射
+✅ 数据完整性:保留所有原始信息(轨迹、切线等)
+✅ 支持吊装路径:扩展了 LiftPoint/DescendPoint/HoverPoint 等节点类型
+✅ 符合 Schema:遵循 TransportPathOrders.xsd 规范
+
+您需要:
+1. 把上面的 C# 或 Python 代码集成到您的程序中(或直接使用插件的导出功能)
+2. 部署 XSD 文件到 Apriso
+3. 配置简单的路由规则
+4. 开发 Job Action 处理程序
+
+> 注:插件已内置 ExportToDelmiaAprisoXml() 方法,在导出对话框中选择 `.delmiaxml` 格式即可直接生成符合该 Schema 的 XML 文件。
\ No newline at end of file
diff --git a/doc/requirement/todo_features.md b/doc/requirement/todo_features.md
index 8f9e920..9eb638c 100644
--- a/doc/requirement/todo_features.md
+++ b/doc/requirement/todo_features.md
@@ -2,6 +2,12 @@
## 功能点
+### [2026/3/9]
+
+1. [x] (功能)支持记录并查看路径文件操作的历史记录
+2. [x] (优化)物流自定义类别配置,简化并合并到系统配置中
+3. [x] (优化)给版本号增加时间戳(增加Build号)
+
### [2026/2/24]
1. [x] (优化)吊装路径支持多个水平层
diff --git a/src/Commands/ExportPathCommand.cs b/src/Commands/ExportPathCommand.cs
index 12faa60..4ec48aa 100644
--- a/src/Commands/ExportPathCommand.cs
+++ b/src/Commands/ExportPathCommand.cs
@@ -154,6 +154,9 @@ namespace NavisworksTransport.Commands
case ExportFormat.Csv:
expectedExtensions = new[] { ".csv" };
break;
+ case ExportFormat.DelmiaApriso:
+ expectedExtensions = new[] { ".aprxml" };
+ break;
default:
expectedExtensions = new[] { ".xml" };
break;
@@ -440,9 +443,14 @@ namespace NavisworksTransport.Commands
{
success = _pathDataManager.ExportToCsv(pathsToExport, _parameters.OutputFilePath, _parameters.ExportSettings);
}
+ else if (_parameters.ExportFormat == ExportFormat.DelmiaApriso)
+ {
+ // DELMIA Apriso格式(TransportPathOrders Schema)
+ success = _pathDataManager.ExportToDelmiaAprisoXml(pathsToExport, _parameters.OutputFilePath, _parameters.ExportSettings);
+ }
else
{
- // DELMIA格式
+ // 旧版DELMIA格式
success = _pathDataManager.ExportDelmiaFormat(pathsToExport, _parameters.OutputFilePath, _parameters.ExportSettings);
}
diff --git a/src/Core/PathDataManager.cs b/src/Core/PathDataManager.cs
index a26d1ce..c797993 100644
--- a/src/Core/PathDataManager.cs
+++ b/src/Core/PathDataManager.cs
@@ -766,6 +766,378 @@ namespace NavisworksTransport
}
}
+ ///
+ /// 导出路径到DELMIA Apriso XML格式(符合TransportPathOrders Schema)
+ ///
+ /// 路径集合
+ /// 文件路径
+ /// 导出设置
+ /// 是否成功
+ public bool ExportToDelmiaAprisoXml(List 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;
+ }
+ }
+
+ ///
+ /// 创建DELMIA Apriso格式的PathOrder元素
+ ///
+ 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;
+ }
+
+ ///
+ /// 将PathPointType映射到DELMIA NodeType枚举
+ /// 支持吊装路径特殊节点类型:LiftPoint, DescendPoint, HoverPoint
+ ///
+ 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";
+ }
+ }
+
+ ///
+ /// 将PathType映射到DELMIA PathTypeEnum
+ ///
+ private string MapToDelmiaPathType(PathType pathType)
+ {
+ switch (pathType)
+ {
+ case PathType.Ground:
+ return "Ground";
+ case PathType.Rail:
+ case PathType.Hoisting:
+ return "Aerial";
+ default:
+ return "Mixed";
+ }
+ }
+
+ ///
+ /// 将PathSegmentType映射到DELMIA EdgeTypeEnum
+ /// 支持吊装路径特殊边类型:vertical, suspension, hoisting
+ ///
+ 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";
+ }
+ }
+
///
/// 获取默认导出路径
///
@@ -1279,7 +1651,8 @@ namespace NavisworksTransport
Json,
Csv,
Delmia,
- Navisworks
+ Navisworks,
+ DelmiaApriso
}
///
diff --git a/src/PathPlanning/FlexNet.BusinessFacade.Logistics.TransportPath.xsd b/src/PathPlanning/FlexNet.BusinessFacade.Logistics.TransportPath.xsd
new file mode 100644
index 0000000..ecd8e68
--- /dev/null
+++ b/src/PathPlanning/FlexNet.BusinessFacade.Logistics.TransportPath.xsd
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs
index 58d036c..84139bf 100644
--- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs
+++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs
@@ -2977,7 +2977,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
var saveFileDialog = new SaveFileDialog
{
Title = "导出全部路径",
- Filter = "XML文件 (*.xml)|*.xml|JSON文件 (*.json)|*.json|CSV文件 (*.csv)|*.csv|所有文件 (*.*)|*.*",
+ Filter = "XML文件 (*.xml)|*.xml|JSON文件 (*.json)|*.json|CSV文件 (*.csv)|*.csv|DELMIA Apriso XML (*.aprxml)|*.aprxml|所有文件 (*.*)|*.*",
FilterIndex = 1,
FileName = $"路径导出_{DateTime.Now:yyyyMMdd_HHmmss}",
DefaultExt = "xml",
@@ -2987,19 +2987,21 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (saveFileDialog.ShowDialog() == true)
{
var filePath = saveFileDialog.FileName;
- var extension = Path.GetExtension(filePath).ToLower();
-
- // 确定导出格式
+
+ // 确定导出格式(通过FilterIndex判断,因为DELMIA Apriso和普通XML都是.xml扩展名)
ExportFormat exportFormat;
- switch (extension)
+ switch (saveFileDialog.FilterIndex)
{
- case ".json":
+ case 2: // JSON文件
exportFormat = ExportFormat.Json;
break;
- case ".csv":
+ case 3: // CSV文件
exportFormat = ExportFormat.Csv;
break;
- default:
+ case 4: // DELMIA Apriso XML
+ exportFormat = ExportFormat.DelmiaApriso;
+ break;
+ default: // XML文件或其他
exportFormat = ExportFormat.Xml;
break;
}
@@ -3065,7 +3067,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
var saveFileDialog = new SaveFileDialog
{
Title = $"导出选中路径: {SelectedPathRoute?.Name}",
- Filter = "XML文件 (*.xml)|*.xml|JSON文件 (*.json)|*.json|CSV文件 (*.csv)|*.csv|所有文件 (*.*)|*.*",
+ Filter = "XML文件 (*.xml)|*.xml|JSON文件 (*.json)|*.json|CSV文件 (*.csv)|*.csv|DELMIA Apriso XML (*.aprxml)|*.aprxml|所有文件 (*.*)|*.*",
FilterIndex = 1,
FileName = SelectedPathRoute?.Name?.Replace(" ", "_") ?? "路径",
DefaultExt = "xml",
@@ -3075,19 +3077,21 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (saveFileDialog.ShowDialog() == true)
{
var filePath = saveFileDialog.FileName;
- var extension = Path.GetExtension(filePath).ToLower();
-
- // 确定导出格式
+
+ // 确定导出格式(通过FilterIndex判断,因为DELMIA Apriso和普通XML都是.xml扩展名)
ExportFormat exportFormat;
- switch (extension)
+ switch (saveFileDialog.FilterIndex)
{
- case ".json":
+ case 2: // JSON文件
exportFormat = ExportFormat.Json;
break;
- case ".csv":
+ case 3: // CSV文件
exportFormat = ExportFormat.Csv;
break;
- default:
+ case 4: // DELMIA Apriso XML
+ exportFormat = ExportFormat.DelmiaApriso;
+ break;
+ default: // XML文件或其他
exportFormat = ExportFormat.Xml;
break;
}