597 lines
31 KiB
C#
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);
|
|
}
|
|
}
|