RobotNet/RobotNet.RobotManager/Services/PathPlanner.cs
2025-10-15 15:15:53 +07:00

352 lines
15 KiB
C#

using RobotNet.MapShares;
using RobotNet.MapShares.Dtos;
using RobotNet.MapShares.Enums;
using RobotNet.RobotManager.Services.Planner;
using RobotNet.RobotManager.Services.Planner.Space;
using RobotNet.RobotShares.Enums;
using RobotNet.Shares;
using System.Collections.Generic;
namespace RobotNet.RobotManager.Services;
public enum PathPlanningType
{
None,
Angle,
StartDirection,
EndDirection,
}
public class PathPlanner(IConfiguration Configuration, MapManager MapManager)
{
private readonly double ResolutionSplit = Configuration.GetValue("PathPlanning:ResolutionSplit", 0.1);
private readonly PathPlanningType PathPlanningType = Configuration.GetValue("PathPlanning:Type", PathPlanningType.None);
private readonly RobotDirection StartDirection = Configuration.GetValue("PathPlanning:StartDirection", RobotDirection.NONE);
private readonly RobotDirection EndDirection = Configuration.GetValue("PathPlanning:EndDirection", RobotDirection.NONE);
public async Task<MessageResult<(NodeDto[] Nodes, EdgeDto[] Edges)>> Planning(double xRef, double yRef, double thetaRef, NavigationType type, Guid mapId, string nodeName)
{
var map = await MapManager.GetMapData(mapId);
if (!map.IsSuccess) return new(false, map.Message);
if (map.Data is null) return new(false, "Dữ liệu bản đồ có lỗi");
var goalNode = map.Data.Nodes.FirstOrDefault(n => n.Name == nodeName);
if (goalNode is null) return new(false, "Đích đến không tồn tại");
PathPlannerManager PathPlannerManager = new();
var PathPlanner = PathPlannerManager.GetPathPlanningService(type);
PathPlanner.SetData(map.Data.Nodes, map.Data.Edges);
if (type == NavigationType.Forklift) return PathPlanner.PathPlanningWithAngle(xRef, yRef, thetaRef, goalNode.Id);
else
{
return PathPlanningType switch
{
PathPlanningType.None => PathPlanner.PathPlanning(xRef, yRef, thetaRef, goalNode.Id),
PathPlanningType.Angle => PathPlanner.PathPlanningWithAngle(xRef, yRef, thetaRef, goalNode.Id),
PathPlanningType.StartDirection => PathPlanner.PathPlanningWithStartDirection(xRef, yRef, thetaRef, goalNode.Id, StartDirection),
PathPlanningType.EndDirection => PathPlanner.PathPlanningWithFinalDirection(xRef, yRef, thetaRef, goalNode.Id, EndDirection),
_ => PathPlanner.PathPlanning(xRef, yRef, thetaRef, goalNode.Id),
};
}
}
public async Task<MessageResult<(NodeDto[] Nodes, EdgeDto[] Edges)>> Planning(Guid startNodeId, double thetaRef, NavigationType type, Guid mapId, Guid goalId)
{
var map = await MapManager.GetMapData(mapId);
if (!map.IsSuccess) return new(false, map.Message);
if (map.Data is null) return new(false, "Dữ liệu bản đồ có lỗi");
PathPlannerManager PathPlannerManager = new();
var PathPlanner = PathPlannerManager.GetPathPlanningService(type);
PathPlanner.SetData(map.Data.Nodes, map.Data.Edges);
if (type == NavigationType.Forklift) return PathPlanner.PathPlanningWithAngle(startNodeId, thetaRef, goalId);
else
{
return PathPlanningType switch
{
PathPlanningType.None => PathPlanner.PathPlanning(startNodeId, thetaRef, goalId),
PathPlanningType.Angle => PathPlanner.PathPlanningWithAngle(startNodeId, thetaRef, goalId),
PathPlanningType.StartDirection => PathPlanner.PathPlanningWithStartDirection(startNodeId, thetaRef, goalId, StartDirection),
PathPlanningType.EndDirection => PathPlanner.PathPlanningWithFinalDirection(startNodeId, thetaRef, goalId, EndDirection),
_ => PathPlanner.PathPlanning(startNodeId, thetaRef, goalId),
};
}
}
public static MessageResult<NodeDto[]> PathSplit(NodeDto[] nodes, EdgeDto[] edges, double resolutionSplit)
{
if (nodes.Length < 1 || edges.Length == 0) return new(false, "Dữ liệu không hợp lệ");
List<NodeDto> pathSplit = [];
pathSplit.Add(new()
{
Id = nodes[0].Id,
X = nodes[0].X,
Y = nodes[0].Y,
Theta = nodes[0].Theta,
Direction = nodes[0].Direction,
Name = nodes[0].Name,
Actions = "CheckingNode"
});
foreach (var edge in edges)
{
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 new(false, "Dữ liệu lỗi: Điểm đầu cuối của edge không có trong danh sách nodes");
NodeDto controlNode = new();
var EdgeCaculatorModel = new EdgeCaculatorModel()
{
X1 = startNode.X,
Y1 = startNode.Y,
X2 = endNode.X,
Y2 = endNode.Y,
ControlPoint1X = edge.ControlPoint1X,
ControlPoint1Y = edge.ControlPoint1Y,
ControlPoint2X = edge.ControlPoint2X,
ControlPoint2Y = edge.ControlPoint2Y,
TrajectoryDegree = edge.TrajectoryDegree,
};
var length = MapEditorHelper.GetEdgeLength(EdgeCaculatorModel);
if (length <= 0) continue;
double step = resolutionSplit / length;
for (double t = step; t <= 1 - step; t += step)
{
(double x, double y) = MapEditorHelper.Curve(t, EdgeCaculatorModel);
pathSplit.Add(new()
{
Id = Guid.NewGuid(),
X = x,
Y = y,
Theta = startNode.Theta,
Direction = startNode.Direction,
});
}
pathSplit.Add(new()
{
Id = endNode.Id,
X = endNode.X,
Y = endNode.Y,
Theta = endNode.Theta,
Direction = endNode.Direction,
Name = endNode.Name,
Actions = "CheckingNode"
});
}
return new(true) { Data = [.. pathSplit] };
}
public MessageResult<NodeDto[]> PathSplit(NodeDto[] nodes, EdgeDto[] edges)
{
if (nodes.Length < 1 || edges.Length == 0) return new(false, "Dữ liệu không hợp lệ");
List<NodeDto> pathSplit = [];
pathSplit.Add(new()
{
Id = nodes[0].Id,
X = nodes[0].X,
Y = nodes[0].Y,
Theta = nodes[0].Theta,
Direction = nodes[0].Direction,
Name = nodes[0].Name,
Actions = "CheckingNode"
});
foreach (var edge in edges)
{
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 new(false, "Dữ liệu lỗi: Điểm đầu cuối của edge không có trong danh sách nodes");
NodeDto controlNode = new();
var EdgeCaculatorModel = new EdgeCaculatorModel()
{
X1 = startNode.X,
Y1 = startNode.Y,
X2 = endNode.X,
Y2 = endNode.Y,
ControlPoint1X = edge.ControlPoint1X,
ControlPoint1Y = edge.ControlPoint1Y,
ControlPoint2X = edge.ControlPoint2X,
ControlPoint2Y = edge.ControlPoint2Y,
TrajectoryDegree = edge.TrajectoryDegree,
};
var length = MapEditorHelper.GetEdgeLength(EdgeCaculatorModel);
if (length <= 0) continue;
double step = ResolutionSplit / length;
for (double t = step; t <= 1 - step; t += step)
{
(double x, double y) = MapEditorHelper.Curve(t, EdgeCaculatorModel);
pathSplit.Add(new()
{
Id = Guid.NewGuid(),
X = x,
Y = y,
Theta = startNode.Theta,
Direction = startNode.Direction,
});
}
pathSplit.Add(new()
{
Id = endNode.Id,
X = endNode.X,
Y = endNode.Y,
Theta = endNode.Theta,
Direction = endNode.Direction,
Name = endNode.Name,
Actions = "CheckingNode"
});
}
return new(true) { Data = [.. pathSplit] };
}
public static RobotDirection GetRobotStartDirection(NodeDto currentNode, NodeDto nearNode, EdgeDto edge, double robotInNodeAngle)
{
NodeDto NearNode = MapEditorHelper.GetNearByNode(currentNode, nearNode, edge, 0.1);
var RobotNearNode = new NodeDto()
{
X = currentNode.X + Math.Cos(robotInNodeAngle * Math.PI / 180),
Y = currentNode.Y + Math.Sin(robotInNodeAngle * Math.PI / 180),
};
return MapEditorHelper.GetAngle(currentNode, NearNode, RobotNearNode) > 89 ? RobotDirection.BACKWARD : RobotDirection.FORWARD;
}
public static RobotDirection[] GetRobotDirectionInPath(RobotDirection currentDirection, NodeDto[] nodes, EdgeDto[] edges)
{
RobotDirection[] RobotDirectionInNode = new RobotDirection[nodes.Length];
if (nodes.Length > 0) RobotDirectionInNode[0] = currentDirection;
if (nodes.Length > 2)
{
for (int i = 1; i < nodes.Length - 1; i++)
{
NodeDto nearLastNode = MapEditorHelper.GetNearByNode(nodes[i], nodes[i - 1], edges[i - 1], 0.1);
NodeDto nearFutureNode = MapEditorHelper.GetNearByNode(nodes[i], nodes[i + 1], edges[i], 0.1); ;
var angle = MapEditorHelper.GetAngle(nodes[i], nearLastNode, nearFutureNode);
if (angle < 89) RobotDirectionInNode[i] = RobotDirectionInNode[i - 1] == RobotDirection.FORWARD ? RobotDirection.BACKWARD : RobotDirection.FORWARD;
else RobotDirectionInNode[i] = RobotDirectionInNode[i - 1];
}
}
if (nodes.Length > 1) RobotDirectionInNode[^1] = RobotDirectionInNode[^2];
return RobotDirectionInNode;
}
public static double[] GetRobotThetaInPath(NodeDto[] nodes, EdgeDto[] edges)
{
if (nodes.Length < 2 || edges.Length < 1 || nodes.Length - 1 != edges.Length) return [];
double[] RobotThetaInNode = new double[nodes.Length];
if (nodes.Length > 1)
{
for (int i = 0; i < nodes.Length - 1; i++)
{
NodeDto nearFutureNode = MapEditorHelper.GetNearByNode(nodes[i], nodes[i + 1], edges[i], 0.1);
var angleForward = Math.Atan2(nearFutureNode.Y - nodes[i].Y, nearFutureNode.X - nodes[i].X) * 180 / Math.PI;
var angleBackward = Math.Atan2(nodes[i].Y - nearFutureNode.Y, nodes[i].X - nearFutureNode.X) * 180 / Math.PI;
RobotThetaInNode[i] = nodes[i].Direction == Direction.FORWARD ? angleForward : angleBackward;
}
RobotThetaInNode[^1] = RobotThetaInNode[^2];
}
return RobotThetaInNode;
}
public static NodeDto[] CalculatorDirection(NodeDto[] nodes, EdgeDto[] edges) => CalculatorDirection(RobotDirection.FORWARD, nodes, edges);
public static NodeDto[] CalculatorDirection(RobotDirection startDirection, NodeDto[] nodes, EdgeDto[] edges)
{
var directions = GetRobotDirectionInPath(startDirection, nodes, edges);
NodeDto[] returnNodes = [.. nodes];
for (int i = 0; i < returnNodes.Length; i++)
{
returnNodes[i].Direction = MapCompute.GetNodeDirection(directions[i]);
}
var thetas = GetRobotThetaInPath(returnNodes, edges);
for (int i = 0; i < returnNodes.Length; i++)
{
returnNodes[i].Theta = thetas[i];
}
return returnNodes;
}
public static NodeDto[] CalculatorDirection(double theta, NodeDto[] nodes, EdgeDto[] edges)
{
if (nodes.Length < 2) return nodes;
var RobotNearNode = new NodeDto()
{
X = nodes[0].X + Math.Cos(theta * Math.PI / 180),
Y = nodes[0].Y + Math.Sin(theta * Math.PI / 180),
};
nodes[0].Direction = MapEditorHelper.GetAngle(nodes[0], RobotNearNode, nodes[1]) > 89 ? MapShares.Enums.Direction.BACKWARD : MapShares.Enums.Direction.FORWARD;
var directions = GetRobotDirectionInPath(MapCompute.GetRobotDirection(nodes[0].Direction), nodes, edges);
NodeDto[] returnNodes = [.. nodes];
for (int i = 0; i < returnNodes.Length; i++)
{
returnNodes[i].Direction = MapCompute.GetNodeDirection(directions[i]);
}
var thetas = GetRobotThetaInPath(returnNodes, edges);
for (int i = 0; i < returnNodes.Length; i++)
{
returnNodes[i].Theta = thetas[i];
}
return returnNodes;
}
public static NodeDto[] CalculatorDirection(double theta, NodeDto[] nodes)
{
if (nodes.Length < 2) return nodes;
var RobotNearNode = new NodeDto()
{
X = nodes[0].X + Math.Cos(theta * Math.PI / 180),
Y = nodes[0].Y + Math.Sin(theta * Math.PI / 180),
};
nodes[0].Direction = MapEditorHelper.GetAngle(nodes[0], RobotNearNode, nodes[1]) > 89 ? MapShares.Enums.Direction.BACKWARD : MapShares.Enums.Direction.FORWARD;
for (int i = 1; i < nodes.Length - 1; i++)
{
if (nodes[i].X == nodes[i - 1].X && nodes[i].Y == nodes[i - 1].Y) nodes[i].Direction = nodes[i - 1].Direction;
else if (nodes[i].X == nodes[i + 1].X && nodes[i].Y == nodes[i + 1].Y) nodes[i].Direction = nodes[i - 1].Direction;
else
{
var angle = MapEditorHelper.GetAngle(nodes[i], nodes[i - 1], nodes[i + 1]);
nodes[i].Direction = angle > 89 ? nodes[i - 1].Direction : (nodes[i - 1].Direction == MapShares.Enums.Direction.FORWARD ? MapShares.Enums.Direction.BACKWARD : MapShares.Enums.Direction.FORWARD);
}
}
nodes[^1].Direction = nodes[^2].Direction;
return nodes;
}
public async Task<MessageResult<Dictionary<Guid, ZoneDto[]>>> GetZones(Guid mapId, NodeDto[] nodes)
{
var map = await MapManager.GetMapData(mapId);
if (!map.IsSuccess) return new(false, map.Message);
if (map.Data is null) return new(false, "Dữ liệu bản đồ có lỗi");
Dictionary<Guid, ZoneDto[]> NodeInZones = [];
foreach (var node in nodes)
{
List<ZoneDto> zones = [];
foreach(var zone in map.Data.Zones)
{
if (MapEditorHelper.IsPointInside(node.X, node.Y, zone)) zones.Add(zone);
}
if (zones.Count > 0) NodeInZones.Add(node.Id, [..zones]);
}
return new(true, "") { Data = NodeInZones };
}
public static ZoneDto[] GetZones(NodeDto[] nodes, Dictionary<Guid, ZoneDto[]> zones)
{
List<ZoneDto> returnZones = [];
foreach (var node in nodes)
{
if (zones.TryGetValue(node.Id, out ZoneDto[]? zone) && zone is not null && zone.Length > 0)
returnZones.AddRange(zone.Where(z => !returnZones.Any(zo => zo.Id == z.Id)));
}
return [.. returnZones];
}
}