1807 lines
79 KiB
C#
1807 lines
79 KiB
C#
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);
|
||
}
|
||
}
|
||
}
|