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 Nodes = []; private List 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 nodeplanning, List 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 nodes, List 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 GetEdgesPlanning(List nodes) { var EdgesPlanning = new List(); 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]), }; } } }