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

311 lines
17 KiB
C#

using RobotNet.MapShares;
using RobotNet.MapShares.Dtos;
using RobotNet.MapShares.Enums;
using RobotNet.RobotManager.Services.Planner.AStar;
using RobotNet.RobotManager.Services.Planner.Fokrlift;
using RobotNet.RobotManager.Services.Planner.Space;
using RobotNet.RobotShares.Enums;
using RobotNet.RobotShares.Models;
using RobotNet.Shares;
namespace RobotNet.RobotManager.Services.Planner.ForkliftV2;
public class ForkLiftPathPlannerV2 : IPathPlanner
{
private List<NodeDto> Nodes = [];
private List<EdgeDto> Edges = [];
private const double ReverseDirectionAngleDegree = 80;
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;
}
public static RobotDirection[] GetRobotDirectionInPath(RobotDirection currentDirection, List<NodeDto> nodeplanning, List<EdgeDto> edgePlanning)
{
RobotDirection[] RobotDirectionInNode = new RobotDirection[nodeplanning.Count];
if (nodeplanning.Count > 0) RobotDirectionInNode[0] = currentDirection;
if (nodeplanning.Count > 2)
{
for (int i = 1; i < nodeplanning.Count - 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 < ReverseDirectionAngleDegree) RobotDirectionInNode[i] = RobotDirectionInNode[i - 1] == RobotDirection.FORWARD ? RobotDirection.BACKWARD : RobotDirection.FORWARD;
else RobotDirectionInNode[i] = RobotDirectionInNode[i - 1];
}
}
if (nodeplanning.Count > 1) RobotDirectionInNode[^1] = RobotDirectionInNode[^2];
return RobotDirectionInNode;
}
public static RobotDirection GetRobotDirection(NodeDto currentNode, NodeDto nearNode, EdgeDto edge, double robotInNodeAngle, bool isReverse)
{
NodeDto NearNode = MapEditorHelper.GetNearByNode(currentNode, nearNode, edge, Ratio);
var RobotNearNode = new NodeDto()
{
X = currentNode.X + Math.Cos(robotInNodeAngle * Math.PI / 180),
Y = currentNode.Y + Math.Sin(robotInNodeAngle * Math.PI / 180),
};
var angle = MapEditorHelper.GetAngle(currentNode, NearNode, RobotNearNode);
if (angle > ReverseDirectionAngleDegree) return isReverse ? RobotDirection.BACKWARD : RobotDirection.FORWARD;
else return isReverse ? RobotDirection.FORWARD : RobotDirection.BACKWARD;
}
private static NodeDto[] CalculatorDirection(List<NodeDto> nodes, List<EdgeDto> edges, RobotDirection currentDirection)
{
var FinalDirection = GetRobotDirectionInPath(currentDirection, nodes, edges);
NodeDto[] returnNodes = [.. nodes];
foreach (var item in returnNodes)
{
item.Direction = MapCompute.GetNodeDirection(FinalDirection[nodes.IndexOf(item)]);
}
return returnNodes;
}
private List<EdgeDto> GetEdgesPlanning(List<NodeDto> nodes)
{
var EdgesPlanning = new List<EdgeDto>();
for (int i = 0; i < nodes.Count - 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;
}
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}]") { Data = ([goal], []) };
if (Path.Data.Count == 1) return new(true, "Robot đang đứng ở điểm đích") { Data = ([], []) };
var edgeplannings = GetEdgesPlanning([.. Path.Data]);
return new(true) { Data = ([.. CalculatorDirection(Path.Data, edgeplannings, MapCompute.GetRobotDirection(Path.Data[0].Direction))], [.. edgeplannings]), };
}
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 SSEAStarPathPlanner = new SSEAStarPathPlanner(Nodes, Edges);
var Path = SSEAStarPathPlanner.PlanningWithGoalAngle(x,
y,
theta,
new NodeDto() { Id = goal.Id, Name = goal.Name, X = goal.X, Y = goal.Y, Theta = goal.Theta },
Options.LimitDistanceToEdge,
Options.LimitDistanceToNode,
cancellationToken);
if (!Path.IsSuccess|| Path.Data is null || Path.Data.Count < 1)
{
var ForkliftV1 = new ForkliftPathPlanner();
ForkliftV1.SetData([.. Nodes], [.. Edges]);
return ForkliftV1.PathPlanningWithAngle(x,
y,
theta,
goal.Id,
cancellationToken);
}
else
{
if (Path.Data.Count == 1) return new(true, $"Robot đang đứng tại điểm đích [{goal.X}, {goal.Y}], robot: [{x}, {y}, {theta}]") { Data = ([goal], [])};
var edgeplannings = GetEdgesPlanning(Path.Data);
if (edgeplannings.Count < 1) return new(false, $"Không thể lấy ra các đoạn thuộc tuyến đường đã tìm ra");
return new(true) { Data = ([.. CalculatorDirection(Path.Data, edgeplannings, MapCompute.GetRobotDirection(Path.Data[0].Direction))], [.. edgeplannings]), };
}
}
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 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 SSEAStarPathPlanner = new SSEAStarPathPlanner(Nodes, Edges);
var Path = SSEAStarPathPlanner.PlanningWithFinalDirection(x,
y,
theta,
new NodeDto() { Id = goal.Id, Name = goal.Name, X = goal.X, Y = goal.Y },
goalDirection,
Options.LimitDistanceToEdge,
Options.LimitDistanceToNode,
cancellationToken);
if (!Path.IsSuccess || Path.Data is null || Path.Data.Count < 1)
{
var ForkliftV1 = new ForkliftPathPlanner();
ForkliftV1.SetData([.. Nodes], [.. Edges]);
return ForkliftV1.PathPlanningWithFinalDirection(x,
y,
theta,
goal.Id,
goalDirection,
cancellationToken);
}
else
{
if (Path.Data.Count == 1) return new(true, $"Robot đang đứng tại điểm đích [{goal.X}, {goal.Y}], robot: [{x}, {y}, {theta}]") { Data = ([goal], []) };
var edgeplannings = GetEdgesPlanning(Path.Data);
if (edgeplannings.Count < 1) return new(false, $"Không thể lấy ra các đoạn thuộc tuyến đường đã tìm ra");
return new(true) { Data = ([.. CalculatorDirection(Path.Data, edgeplannings, MapCompute.GetRobotDirection(Path.Data[0].Direction))], [.. edgeplannings]), };
}
}
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}]") { Data = ([goal], []) };
if (Path.Data.Length == 1) return new(true, "Robot đang đứng ở điểm đích") { Data = ([], []) };
var edgeplannings = GetEdgesPlanning([.. Path.Data]);
return new(true) { Data = ([.. CalculatorDirection([..Path.Data], edgeplannings, MapCompute.GetRobotDirection(Path.Data[0].Direction))], [.. 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 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 SSEAStarPathPlanner = new SSEAStarPathPlanner(Nodes, Edges);
var Path = SSEAStarPathPlanner.PlanningWithGoalAngle(startNode,
theta,
goal,
cancellationToken);
if (!Path.IsSuccess || Path.Data is null || Path.Data.Length < 1)
{
var ForkliftV1 = new ForkliftPathPlanner();
ForkliftV1.SetData([.. Nodes], [.. Edges]);
return ForkliftV1.PathPlanningWithAngle(startNodeId,
theta,
goal.Id,
cancellationToken);
}
else
{
if (Path.Data.Length == 1) return new(true, $"Robot đang đứng tại điểm đích [{goal.X}, {goal.Y}], robot: [{startNode.Name} - {startNode.Id}]") { Data = ([goal], []) };
var edgeplannings = GetEdgesPlanning([.. Path.Data]);
if (edgeplannings.Count < 1) return new(false, $"Không thể lấy ra các đoạn thuộc tuyến đường đã tìm ra");
return new(true) { Data = ([.. CalculatorDirection([.. Path.Data], edgeplannings, MapCompute.GetRobotDirection(Path.Data[0].Direction))], [.. edgeplannings]), };
}
}
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 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 SSEAStarPathPlanner = new SSEAStarPathPlanner(Nodes, Edges);
var Path = SSEAStarPathPlanner.PlanningWithFinalDirection(startNode,
theta,
goal,
goalDirection,
cancellationToken);
if (!Path.IsSuccess || Path.Data is null || Path.Data.Length < 1)
{
var ForkliftV1 = new ForkliftPathPlanner();
ForkliftV1.SetData([.. Nodes], [.. Edges]);
return ForkliftV1.PathPlanningWithFinalDirection(startNodeId,
theta,
goal.Id,
goalDirection,
cancellationToken);
}
else
{
if (Path.Data.Length == 1) return new(true, $"Robot đang đứng tại điểm đích [{goal.X}, {goal.Y}], robot: [{startNode.Name} - {startNode.Id}]") { Data = ([goal], []) };
var edgeplannings = GetEdgesPlanning([.. Path.Data]);
if (edgeplannings.Count < 1) return new(false, $"Không thể lấy ra các đoạn thuộc tuyến đường đã tìm ra");
return new(true) { Data = ([.. CalculatorDirection([.. Path.Data], edgeplannings, MapCompute.GetRobotDirection(Path.Data[0].Direction))], [.. edgeplannings]), };
}
}
}