RobotNet/RobotNet.RobotManager/Services/Planner/Fokrlift/ForkliftPathPlanner.cs
2025-10-15 15:15:53 +07:00

597 lines
31 KiB
C#

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<NodeDto> Nodes = [];
private List<EdgeDto> 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<TStructure> 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<TStructure> GetTStructure()
{
List<TStructure> 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<NodeDto> 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<TStructure> 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<NodeDto> 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<NodeDto> node1 = [];
List<NodeDto> 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<EdgeDto>();
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<NodeDto> 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);
}
}