using RobotNet.MapShares; using RobotNet.MapShares.Dtos; using RobotNet.MapShares.Enums; using RobotNet.RobotManager.Services.Planner.AStar; using RobotNet.RobotManager.Services.Planner.Space; using RobotNet.RobotShares.Enums; using RobotNet.Shares; namespace RobotNet.RobotManager.Services.Planner.Fokrlift; public class ForkliftPathPlanner : IPathPlanner { private List Nodes = []; private List Edges = []; private const double ReverseDirectionAngleDegree = 89; private const double RobotDirectionWithAngle = 90; private const double Ratio = 0.1; private readonly PathPlanningOptions Options = new() { LimitDistanceToEdge = 1, LimitDistanceToNode = 0.3, ResolutionSplit = 0.1, }; public void SetData(NodeDto[] nodes, EdgeDto[] edges) { Nodes = [.. nodes]; Edges = [.. edges]; } public void SetOptions(PathPlanningOptions options) { Options.LimitDistanceToNode = options.LimitDistanceToNode; Options.LimitDistanceToEdge = options.LimitDistanceToEdge; Options.ResolutionSplit = options.ResolutionSplit; } private static bool TStructureExisted(List TStructures, NodeDto node1, NodeDto node2, NodeDto node3) { var TStructureExistedStep1 = TStructures.Where(ts => ts.Node1 == node1 || ts.Node2 == node1 || ts.Node3 == node1).ToList(); if (TStructureExistedStep1.Count != 0) { var TStructureExistedStep2 = TStructureExistedStep1.Where(ts => ts.Node1 == node2 || ts.Node2 == node2 || ts.Node3 == node2).ToList(); if (TStructureExistedStep2.Count != 0) { var TStructureExistedStep3 = TStructureExistedStep2.Where(ts => ts.Node1 == node3 || ts.Node2 == node3 || ts.Node3 == node3).ToList(); if (TStructureExistedStep3.Count != 0) return true; } } return false; } private List GetTStructure() { List TStructures = []; foreach (var node in Nodes) { var inEdges = Edges.Where(edge => edge.StartNodeId == node.Id || edge.EndNodeId == node.Id).ToList(); if (inEdges.Count < 2) continue; List inNodes = []; foreach (var edge in inEdges) { var endNode = Nodes.FirstOrDefault(n => n.Id == (node.Id == edge.EndNodeId ? edge.StartNodeId : edge.EndNodeId)); if (endNode is null) continue; inNodes.Add(endNode); } for (int i = 0; i < inNodes.Count - 1; i++) { for (int j = i + 1; j < inNodes.Count; j++) { if (TStructureExisted(TStructures, node, inNodes[i], inNodes[j])) continue; var edgeT = Edges.FirstOrDefault(e => (e.StartNodeId == inNodes[i].Id && e.EndNodeId == inNodes[j].Id) || (e.EndNodeId == inNodes[i].Id && e.StartNodeId == inNodes[j].Id)); var edge1 = inEdges.FirstOrDefault(edge => edge.StartNodeId == inNodes[i].Id || edge.EndNodeId == inNodes[i].Id); var edge2 = inEdges.FirstOrDefault(edge => edge.StartNodeId == inNodes[j].Id || edge.EndNodeId == inNodes[j].Id); if (edgeT is null || edge1 is null || edge2 is null) continue; if (edgeT.TrajectoryDegree == TrajectoryDegree.One && edge1.TrajectoryDegree == TrajectoryDegree.One && edge2.TrajectoryDegree == TrajectoryDegree.One) continue; TStructures.Add(new() { Node1 = node, Node2 = inNodes[i], Node3 = inNodes[j], Edge12 = edge1, Edge13 = edge2, Edge23 = edgeT, }); } } } return TStructures; } public static RobotDirection[] GetRobotDirectionInPath(RobotDirection currentDirection, NodeDto[] nodeplanning, EdgeDto[] edgePlanning, double reverseDirectionAngle) { RobotDirection[] RobotDirectionInNode = new RobotDirection[nodeplanning.Length]; if (nodeplanning.Length > 0) RobotDirectionInNode[0] = currentDirection; if (nodeplanning.Length > 2) { for (int i = 1; i < nodeplanning.Length - 1; i++) { NodeDto startNode = MapEditorHelper.GetNearByNode(nodeplanning[i], nodeplanning[i - 1], edgePlanning[i - 1], Ratio); NodeDto endNode = MapEditorHelper.GetNearByNode(nodeplanning[i], nodeplanning[i + 1], edgePlanning[i], Ratio); ; var angle = MapEditorHelper.GetAngle(nodeplanning[i], startNode, endNode); if (angle < reverseDirectionAngle) RobotDirectionInNode[i] = RobotDirectionInNode[i - 1] == RobotDirection.FORWARD ? RobotDirection.BACKWARD : RobotDirection.FORWARD; else RobotDirectionInNode[i] = RobotDirectionInNode[i - 1]; } } if (nodeplanning.Length > 1) RobotDirectionInNode[^1] = RobotDirectionInNode[^2]; return RobotDirectionInNode; } public static RobotDirection GetRobotDirection(NodeDto currentNode, NodeDto futureNode, EdgeDto edge, double robotTheta, double reverseDirectionAngle, bool inGoal) { NodeDto NearNode = MapEditorHelper.GetNearByNode(currentNode, futureNode, edge, Ratio); var RobotDirectionNearNode = new NodeDto() { X = currentNode.X + Math.Cos(robotTheta * Math.PI / 180), Y = currentNode.Y + Math.Sin(robotTheta * Math.PI / 180), }; var angle = MapEditorHelper.GetAngle(currentNode, NearNode, RobotDirectionNearNode); if (angle > reverseDirectionAngle) return inGoal ? RobotDirection.FORWARD : RobotDirection.BACKWARD; else return inGoal ? RobotDirection.BACKWARD : RobotDirection.FORWARD; } private static double GetNodeAngle(NodeDto node, NodeDto lastNode, EdgeDto edge) { NodeDto NearNode = MapEditorHelper.GetNearByNode(node, lastNode, edge, Ratio); return Math.Atan2(node.Y - NearNode.Y, node.X - NearNode.X) * 180 / Math.PI; } private static (bool IsSuccess, NodeDto? intraNode, TStructure? tstructure) IsReverse(NodeDto currentNode, NodeDto olderNode, NodeDto? futureNode, EdgeDto olderedge, EdgeDto? futureedge, double startAngle, List tstructures) { var tstructures1 = tstructures.Where(t => t.Node1.Id == currentNode.Id || t.Node2.Id == currentNode.Id || t.Node3.Id == currentNode.Id).ToList(); if (tstructures1 is null || tstructures1.Count < 1) return (false, null, null); var tstructures2 = tstructures1.Where(t => t.Node1.Id == olderNode.Id || t.Node2.Id == olderNode.Id || t.Node3.Id == olderNode.Id).ToList(); if (tstructures2 is null || tstructures2.Count < 1) return (false, null, null); foreach (var ts in tstructures2) { var midleReverse = ts.IsDriectionReverse(currentNode, olderNode); var intraNode = ts.GetIntraNode(currentNode, olderNode); if (intraNode is null) continue; if (!ts.IsAccessDirection(olderNode, intraNode) || !ts.IsAccessDirection(intraNode, currentNode)) continue; var CurrentDirection = GetRobotDirection(olderNode, currentNode, olderedge, startAngle, ReverseDirectionAngleDegree, false); var intraEdge = ts.GetEdge(olderNode, intraNode); if (intraEdge is null) continue; var ReverseDirection = GetRobotDirection(olderNode, intraNode, intraEdge, startAngle, ReverseDirectionAngleDegree, false); bool firstReverse = ReverseDirection != CurrentDirection; bool endReverse = false; if (futureNode is not null && futureedge is not null) { startAngle = GetNodeAngle(currentNode, olderNode, olderedge); CurrentDirection = GetRobotDirection(currentNode, futureNode, futureedge, startAngle, ReverseDirectionAngleDegree, true); intraEdge = ts.GetEdge(currentNode, intraNode); if (intraEdge is null) continue; startAngle = GetNodeAngle(currentNode, intraNode, intraEdge); ReverseDirection = GetRobotDirection(currentNode, futureNode, futureedge, startAngle, ReverseDirectionAngleDegree, true); endReverse = ReverseDirection != CurrentDirection; } if (!midleReverse) { if ((!firstReverse && !endReverse) || (firstReverse && endReverse)) continue; } else { if ((firstReverse && !endReverse) || (!firstReverse && endReverse)) continue; } return (true, intraNode, ts); } return (false, null, null); } private List GetIntermediateNode(NodeDto startNode, NodeDto endNode) { var edge1s = Edges.Where(e => e.StartNodeId == startNode.Id || e.EndNodeId == startNode.Id).ToList(); var edge2s = Edges.Where(e => e.StartNodeId == endNode.Id || e.EndNodeId == endNode.Id).ToList(); if (edge1s is null || edge2s is null || edge1s.Count < 2 || edge2s.Count < 2) return []; List node1 = []; List IntermediateNode = []; foreach (var edge1 in edge1s) { if (edge1.TrajectoryDegree != TrajectoryDegree.One) continue; Guid interNodeId = Guid.Empty; if (edge1.StartNodeId == startNode.Id && (edge1.DirectionAllowed == DirectionAllowed.Both || edge1.DirectionAllowed == DirectionAllowed.Forward)) interNodeId = edge1.EndNodeId; else if (edge1.DirectionAllowed == DirectionAllowed.Both || edge1.DirectionAllowed == DirectionAllowed.Forward) interNodeId = edge1.StartNodeId; if (interNodeId == Guid.Empty || interNodeId == endNode.Id) continue; var interNode = Nodes.FirstOrDefault(n => n.Id == interNodeId); if (interNode is null) continue; //(double distance, double x, double y) = PathPlanning.DistanceToRangeSegment(interNode.X, interNode.Y, startNode.X, startNode.Y, endNode.X, endNode.Y); //if (distance < 0.3 && x != startNode.X && x != endNode.X && y != startNode.Y && y != endNode.Y) //{ // node1.Add(interNode); //} node1.Add(interNode); } if (node1.Count == 0) return []; foreach (var edge2 in edge2s) { if (edge2.TrajectoryDegree != TrajectoryDegree.One) continue; Guid interNodeId = Guid.Empty; if (edge2.StartNodeId == endNode.Id && (edge2.DirectionAllowed == DirectionAllowed.Both || edge2.DirectionAllowed == DirectionAllowed.Forward)) interNodeId = edge2.EndNodeId; else if (edge2.DirectionAllowed == DirectionAllowed.Both || edge2.DirectionAllowed == DirectionAllowed.Forward) interNodeId = edge2.StartNodeId; if (interNodeId == Guid.Empty || interNodeId == startNode.Id) continue; var interNode = Nodes.FirstOrDefault(n => n.Id == interNodeId); if (interNode is null) continue; //(double distance, double x, double y) = PathPlanning.DistanceToRangeSegment(interNode.X, interNode.Y, startNode.X, startNode.Y, endNode.X, endNode.Y); //if (distance < 0.3 && x != startNode.X && x != endNode.X && y != startNode.Y && y != endNode.Y) //{ // if (node1.Any(n => n.Id == interNode.Id) && !IntermediateNode.Any(n => n.Id == interNode.Id) && interNode.Id != startNode.Id) // IntermediateNode.Add(interNode); //} if (node1.Any(n => n.Id == interNode.Id) && !IntermediateNode.Any(n => n.Id == interNode.Id) && interNode.Id != startNode.Id) IntermediateNode.Add(interNode); } return IntermediateNode; } private EdgeDto[] GetEdgesPlanning(NodeDto[] nodes) { var EdgesPlanning = new List(); for (int i = 0; i < nodes.Length - 1; i++) { var edge = Edges.FirstOrDefault(e => e.StartNodeId == nodes[i].Id && e.EndNodeId == nodes[i + 1].Id || e.EndNodeId == nodes[i].Id && e.StartNodeId == nodes[i + 1].Id); if (edge is null) { if (i != 0) return []; EdgesPlanning.Add(new EdgeDto() { Id = Guid.NewGuid(), StartNodeId = nodes[i].Id, EndNodeId = nodes[i + 1].Id, DirectionAllowed = DirectionAllowed.Both, TrajectoryDegree = TrajectoryDegree.One, }); continue; } bool isReverse = nodes[i].Id != edge.StartNodeId && edge.TrajectoryDegree == TrajectoryDegree.Three; ; EdgesPlanning.Add(new() { Id = edge.Id, StartNodeId = nodes[i].Id, EndNodeId = nodes[i + 1].Id, DirectionAllowed = edge.DirectionAllowed, TrajectoryDegree = edge.TrajectoryDegree, ControlPoint1X = isReverse ? edge.ControlPoint2X : edge.ControlPoint1X, ControlPoint1Y = isReverse ? edge.ControlPoint2Y : edge.ControlPoint1Y, ControlPoint2X = isReverse ? edge.ControlPoint1X : edge.ControlPoint2X, ControlPoint2Y = isReverse ? edge.ControlPoint1Y : edge.ControlPoint2Y }); } return [.. EdgesPlanning]; } private (NodeDto[] NodesFilter, EdgeDto[] EdgesFilter) FilterPathPlanning(NodeDto[] nodes, EdgeDto[] edges) { if (nodes.Length <= 1 || edges.Length < 1 || nodes.Length - 1 != edges.Length) return ([], []); List nodeFilter = [nodes[0]]; for (int i = 1; i < nodes.Length - 1; i++) { var IntermediateNode = GetIntermediateNode(nodes[i - 1], nodes[i]); if (IntermediateNode is null || IntermediateNode.Count == 0) { nodeFilter.Add(nodes[i]); continue; } if (IntermediateNode.Any(n => n.Id == nodes[i + 1].Id)) { nodeFilter.Add(nodes[i + 1]); i++; } else nodeFilter.Add(nodes[i]); } nodeFilter.Add(nodes[^1]); var edgeFilter = GetEdgesPlanning([.. nodeFilter]); if (nodeFilter.Count - 1 != edgeFilter.Length) return ([], []); return ([.. nodeFilter], [.. edgeFilter]); } private double GetLength(EdgeDto[] edges) { if (edges.Length == 0) return 999; double distance = 0; for (int i = 0; i < edges.Length; i++) { var edge = edges.FirstOrDefault(e => e.Id == edges[i].Id); if (edge is null) return 999; var startNode = Nodes.FirstOrDefault(n => n.Id == edge.StartNodeId); var endNode = Nodes.FirstOrDefault(n => n.Id == edge.EndNodeId); if (startNode is null || endNode is null) return 999; distance += MapEditorHelper.GetEdgeLength(new() { X1 = startNode.X, X2 = endNode.X, Y1 = startNode.Y, Y2 = endNode.Y, TrajectoryDegree = edge.TrajectoryDegree, ControlPoint1X = edge.ControlPoint1X, ControlPoint1Y = edge.ControlPoint1Y, ControlPoint2X = edge.ControlPoint2X, ControlPoint2Y = edge.ControlPoint2Y, }); } return distance; } private static NodeDto[] CalculatorDirection(NodeDto[] nodes, EdgeDto[] edges, RobotDirection currentDirection) { var FinalDirection = GetRobotDirectionInPath(currentDirection, nodes, edges, ReverseDirectionAngleDegree); NodeDto[] returnNodes = [.. nodes]; for (var i = 0; i < returnNodes.Length; i++) { returnNodes[i].Direction = MapCompute.GetNodeDirection(FinalDirection[i]); } return returnNodes; } public MessageResult<(NodeDto[] Nodes, EdgeDto[] Edges)> PathPlanning(double x, double y, double theta, Guid goalId, CancellationToken? cancellationToken = null) { var goal = Nodes.FirstOrDefault(n => n.Id == goalId); if (goal is null) return new(false, $"Đích đến {goalId} không tồn tại trong map"); var AStarPathPlanner = new AStarPathPlanner(Nodes, Edges); var Path = AStarPathPlanner.Planning(x, y, new NodeDto() { Id = goal.Id, Name = goal.Name, X = goal.X, Y = goal.Y }, Options.LimitDistanceToEdge, Options.LimitDistanceToNode, cancellationToken); if (!Path.IsSuccess) return new(false, Path.Message); if (Path.Data is null || Path.Data.Count < 1) return new(false, $"Đường đi đến {goal.Name} - {goal.Id} không tồn tại từ [{x}, {y}, {theta}]"); if (Path.Data.Count == 1) return new(true, "Robot đang đứng ở điểm đích") { Data = ([], []) }; var edgeplannings = GetEdgesPlanning([.. Path.Data]); RobotDirection CurrenDirection = GetRobotDirection(Path.Data[0], Path.Data[1], edgeplannings[0], theta, RobotDirectionWithAngle, false); return new(true) { Data = ([.. CalculatorDirection([.. Path.Data], [.. edgeplannings], CurrenDirection)], [.. edgeplannings]) }; } private MessageResult<(NodeDto[] Nodes, EdgeDto[] Edges)> CheckPathWithFinalDirection(NodeDto[] nodes, EdgeDto[] edges, double currentAngle, RobotDirection goalDirection = RobotDirection.NONE) { if ((nodes[^1].Direction == MapCompute.GetNodeDirection(goalDirection) && GetLength([.. edges]) < 10) || goalDirection == RobotDirection.NONE) return new(true) { Data = FilterPathPlanning([.. nodes], [.. edges]) }; var edgeplannings = edges.ToList(); var nodeplannings = nodes.ToList(); var TStructures = GetTStructure(); Guid LastReverseDirectionId = Guid.Empty; NodeDto LastNodeReverseDirection = new(); for (int i = 1; i < nodeplannings.Count; i++) { if (nodeplannings[i].Direction == Direction.FORWARD) continue; NodeDto? futureNode = null; EdgeDto? futureEdge = null; if (i < nodeplannings.Count - 1) { futureNode = nodeplannings[i + 1]; futureEdge = edgeplannings[i]; } double startAngle = currentAngle; if (i >= 2) startAngle = GetNodeAngle(nodeplannings[i - 1], nodeplannings[i - 2], edgeplannings[i - 2]); (var IsSuccess, var intraNode, var tstructure) = IsReverse(nodeplannings[i], nodeplannings[i - 1], futureNode, edgeplannings[i - 1], futureEdge, startAngle, TStructures); if (!IsSuccess || intraNode is null || tstructure is null) continue; var edge1 = Edges.FirstOrDefault(e => (e.StartNodeId == nodeplannings[i - 1].Id && e.EndNodeId == intraNode.Id) || e.EndNodeId == nodeplannings[i - 1].Id && e.StartNodeId == intraNode.Id); var edge2 = Edges.FirstOrDefault(e => (e.StartNodeId == nodeplannings[i].Id && e.EndNodeId == intraNode.Id) || e.EndNodeId == nodeplannings[i].Id && e.StartNodeId == intraNode.Id); if (edge1 is null || edge2 is null) continue; edgeplannings.RemoveAt(i - 1); edgeplannings.Insert(i - 1, new() { Id = edge1.Id, StartNodeId = nodeplannings[i - 1].Id, EndNodeId = intraNode.Id, TrajectoryDegree = edge1.TrajectoryDegree, DirectionAllowed = edge1.DirectionAllowed, ControlPoint1X = edge1.ControlPoint1X, ControlPoint1Y = edge1.ControlPoint1Y, ControlPoint2X = edge1.ControlPoint2X, ControlPoint2Y = edge1.ControlPoint2Y }); edgeplannings.Insert(i, new() { Id = edge2.Id, StartNodeId = intraNode.Id, EndNodeId = nodeplannings[i].Id, TrajectoryDegree = edge2.TrajectoryDegree, DirectionAllowed = edge2.DirectionAllowed, ControlPoint1X = edge2.ControlPoint1X, ControlPoint1Y = edge2.ControlPoint1Y, ControlPoint2X = edge2.ControlPoint2X, ControlPoint2Y = edge2.ControlPoint2Y }); nodeplannings.Insert(i, intraNode); var directionInPath = GetRobotDirectionInPath(MapCompute.GetRobotDirection(nodeplannings[0].Direction), [.. nodeplannings], [.. edgeplannings], ReverseDirectionAngleDegree); foreach (var node in nodeplannings) { node.Direction = MapCompute.GetNodeDirection(directionInPath[nodeplannings.IndexOf(node)]); } LastReverseDirectionId = tstructure.Id; LastNodeReverseDirection = nodeplannings[i + 1]; i++; } if (nodeplannings[^1].Direction == MapCompute.GetNodeDirection(goalDirection)) return new(true) { Data = FilterPathPlanning([.. nodeplannings], [.. edgeplannings]) }; for (int i = nodeplannings.Count - 1; i > 0; i--) { NodeDto? futureNode = null; EdgeDto? futureEdge = null; if (i < nodeplannings.Count - 1) { futureNode = nodeplannings[i + 1]; futureEdge = edgeplannings[i]; } double startAngle = currentAngle; if (i >= 2) startAngle = GetNodeAngle(nodeplannings[i - 1], nodeplannings[i - 2], edgeplannings[i - 2]); (var IsSuccess, var intraNode, var tstructure) = IsReverse(nodeplannings[i], nodeplannings[i - 1], futureNode, edgeplannings[i - 1], futureEdge, startAngle, TStructures); if (!IsSuccess || intraNode is null || tstructure is null) continue; if (nodeplannings[i - 1].Id == LastNodeReverseDirection.Id) { var edge = Edges.FirstOrDefault(e => (e.StartNodeId == nodeplannings[i - 2].Id && e.EndNodeId == nodeplannings[i].Id) || (e.StartNodeId == nodeplannings[i].Id && e.EndNodeId == nodeplannings[i - 2].Id)); if (edge is null) continue; edgeplannings.Insert(i - 2, new() { Id = edge.Id, StartNodeId = nodeplannings[i - 2].Id, EndNodeId = nodeplannings[i].Id, TrajectoryDegree = edge.TrajectoryDegree, DirectionAllowed = edge.DirectionAllowed, ControlPoint1X = edge.ControlPoint1X, ControlPoint1Y = edge.ControlPoint1Y, ControlPoint2X = edge.ControlPoint2X, ControlPoint2Y = edge.ControlPoint2Y }); edgeplannings.RemoveAt(i); edgeplannings.RemoveAt(i - 1); nodeplannings.RemoveAt(i - 1); } else if (tstructure.Id != LastReverseDirectionId || i < 2) { var edge1 = Edges.FirstOrDefault(e => (e.StartNodeId == nodeplannings[i - 1].Id && e.EndNodeId == intraNode.Id) || e.EndNodeId == nodeplannings[i - 1].Id && e.StartNodeId == intraNode.Id); var edge2 = Edges.FirstOrDefault(e => (e.StartNodeId == nodeplannings[i].Id && e.EndNodeId == intraNode.Id) || e.EndNodeId == nodeplannings[i].Id && e.StartNodeId == intraNode.Id); if (edge1 is null || edge2 is null) continue; edgeplannings.RemoveAt(i - 1); edgeplannings.Insert(i - 1, new() { Id = edge1.Id, StartNodeId = nodeplannings[i - 1].Id, EndNodeId = intraNode.Id, TrajectoryDegree = edge1.TrajectoryDegree, DirectionAllowed = edge1.DirectionAllowed, ControlPoint1X = edge1.ControlPoint1X, ControlPoint1Y = edge1.ControlPoint1Y, ControlPoint2X = edge1.ControlPoint2X, ControlPoint2Y = edge1.ControlPoint2Y, }); edgeplannings.Insert(i, new() { Id = edge2.Id, StartNodeId = intraNode.Id, EndNodeId = nodeplannings[i].Id, TrajectoryDegree = edge2.TrajectoryDegree, DirectionAllowed = edge2.DirectionAllowed, ControlPoint1X = edge2.ControlPoint1X, ControlPoint1Y = edge2.ControlPoint1Y, ControlPoint2X = edge2.ControlPoint2X, ControlPoint2Y = edge2.ControlPoint2Y, }); nodeplannings.Insert(i, intraNode); } else { var edge = Edges.FirstOrDefault(e => (e.StartNodeId == nodeplannings[i - 2].Id && e.EndNodeId == nodeplannings[i].Id) || (e.StartNodeId == nodeplannings[i].Id && e.EndNodeId == nodeplannings[i - 2].Id)); if (edge is null) continue; edgeplannings.Insert(i - 2, new() { Id = edge.Id, StartNodeId = nodeplannings[i - 2].Id, EndNodeId = nodeplannings[i].Id, TrajectoryDegree = edge.TrajectoryDegree, DirectionAllowed = edge.DirectionAllowed, ControlPoint1X = edge.ControlPoint1X, ControlPoint1Y = edge.ControlPoint1Y, ControlPoint2X = edge.ControlPoint2X, ControlPoint2Y = edge.ControlPoint2Y, }); edgeplannings.RemoveAt(i); edgeplannings.RemoveAt(i - 1); nodeplannings.RemoveAt(i - 1); } return new(true) { Data = FilterPathPlanning([.. CalculatorDirection([.. nodeplannings], [.. edgeplannings], MapCompute.GetRobotDirection(nodeplannings[0].Direction))], [.. edgeplannings]) }; } return new(false, $"Đường đi đến đích không thỏa mãn điều kiện"); } public MessageResult<(NodeDto[] Nodes, EdgeDto[] Edges)> PathPlanningWithAngle(double x, double y, double theta, Guid goalId, CancellationToken? cancellationToken = null) { var goal = Nodes.FirstOrDefault(n => n.Id == goalId); if (goal is null) return new(false, $"Đích đến {goalId} không tồn tại trong map"); var basicPath = PathPlanning(x, y, theta, goalId, cancellationToken); if (!basicPath.IsSuccess) return basicPath; if (basicPath.Data.Nodes.Length < 2) return new(true, "Robot đang đứng ở điểm đích") { Data = ([], []) }; RobotDirection goalDirection = GetRobotDirection(basicPath.Data.Nodes[^1], basicPath.Data.Nodes[^2], basicPath.Data.Edges[^1], goal.Theta, RobotDirectionWithAngle, true); return CheckPathWithFinalDirection(basicPath.Data.Nodes, basicPath.Data.Edges, theta, goalDirection); } public MessageResult<(NodeDto[] Nodes, EdgeDto[] Edges)> PathPlanningWithStartDirection(double x, double y, double theta, Guid goalId, RobotDirection startDiretion = RobotDirection.NONE, CancellationToken? cancellationToken = null) { throw new NotImplementedException(); } public MessageResult<(NodeDto[] Nodes, EdgeDto[] Edges)> PathPlanningWithFinalDirection(double x, double y, double theta, Guid goalId, RobotDirection goalDirection = RobotDirection.NONE, CancellationToken? cancellationToken = null) { var basicPath = PathPlanning(x, y, theta, goalId, cancellationToken); if (!basicPath.IsSuccess) return basicPath; if (basicPath.Data.Nodes.Length < 2) return new(true, "Robot đang đứng ở điểm đích") { Data = ([], []) }; return CheckPathWithFinalDirection(basicPath.Data.Nodes, basicPath.Data.Edges, theta, goalDirection); } public MessageResult<(NodeDto[] Nodes, EdgeDto[] Edges)> PathPlanning(Guid startNodeId, double theta, Guid goalId, CancellationToken? cancellationToken = null) { var goal = Nodes.FirstOrDefault(n => n.Id == goalId); if (goal is null) return new(false, $"Đích đến {goalId} không tồn tại trong map"); var startNode = Nodes.FirstOrDefault(n => n.Id == startNodeId); if (startNode is null) return new(false, $"Điểm bắt đầu {startNodeId} không tồn tại trong map"); var AStarPathPlanner = new AStarPathPlanner(Nodes, Edges); var Path = AStarPathPlanner.Planning(startNode, goal, cancellationToken); if (!Path.IsSuccess) return new(false, Path.Message); if (Path.Data is null || Path.Data.Length < 1) return new(false, $"Đường đi đến {goal.Name} - {goal.Id} không tồn tại từ [{startNode.Name} - {startNode.Id}]"); if (Path.Data.Length == 1) return new(true, "Robot đang đứng ở điểm đích") { Data = ([], []) }; var edgeplannings = GetEdgesPlanning([.. Path.Data]); RobotDirection CurrenDirection = GetRobotDirection(Path.Data[0], Path.Data[1], edgeplannings[0], theta, RobotDirectionWithAngle, false); return new(true) { Data = ([.. CalculatorDirection([.. Path.Data], [.. edgeplannings], CurrenDirection)], [.. edgeplannings]) }; } public MessageResult<(NodeDto[] Nodes, EdgeDto[] Edges)> PathPlanningWithAngle(Guid startNodeId, double theta, Guid goalId, CancellationToken? cancellationToken = null) { var goal = Nodes.FirstOrDefault(n => n.Id == goalId); if (goal is null) return new(false, $"Đích đến {goalId} không tồn tại trong map"); var basicPath = PathPlanning(startNodeId, theta, goalId, cancellationToken); if (!basicPath.IsSuccess) return basicPath; if (basicPath.Data.Nodes.Length < 2) return new(true, "Robot đang đứng ở điểm đích") { Data = ([], []) }; RobotDirection goalDirection = GetRobotDirection(basicPath.Data.Nodes[^1], basicPath.Data.Nodes[^2], basicPath.Data.Edges[^1], goal.Theta, RobotDirectionWithAngle, true); return CheckPathWithFinalDirection(basicPath.Data.Nodes, basicPath.Data.Edges, theta, goalDirection); } public MessageResult<(NodeDto[] Nodes, EdgeDto[] Edges)> PathPlanningWithStartDirection(Guid startNodeId, double theta, Guid goalId, RobotDirection startDiretion = RobotDirection.NONE, CancellationToken? cancellationToken = null) { throw new NotImplementedException(); } public MessageResult<(NodeDto[] Nodes, EdgeDto[] Edges)> PathPlanningWithFinalDirection(Guid startNodeId, double theta, Guid goalId, RobotDirection goalDirection = RobotDirection.NONE, CancellationToken? cancellationToken = null) { var basicPath = PathPlanning(startNodeId, theta, goalId, cancellationToken); if (!basicPath.IsSuccess) return basicPath; if (basicPath.Data.Nodes.Length < 2) return new(true, "Robot đang đứng ở điểm đích") { Data = ([], []) }; return CheckPathWithFinalDirection(basicPath.Data.Nodes, basicPath.Data.Edges, theta, goalDirection); } }