first commit -push
This commit is contained in:
269
RobotNet.RobotManager/Services/Traffic/Agent.cs
Normal file
269
RobotNet.RobotManager/Services/Traffic/Agent.cs
Normal file
@@ -0,0 +1,269 @@
|
||||
using RobotNet.MapShares;
|
||||
using RobotNet.MapShares.Dtos;
|
||||
using RobotNet.RobotManager.Services.Planner.Space;
|
||||
using RobotNet.RobotShares.Dtos;
|
||||
using RobotNet.RobotShares.Enums;
|
||||
|
||||
namespace RobotNet.RobotManager.Services.Traffic;
|
||||
|
||||
|
||||
public class Agent
|
||||
{
|
||||
public IRobotController Robot { get; set; } = null!;
|
||||
public AgentModel AgentModel { get; set; } = new();
|
||||
public Guid MapId { get; set; }
|
||||
public int InNodeIndex { get; set; }
|
||||
public TrafficNodeDto InNode { get; set; } = new();
|
||||
public int ReleaseNodeIndex => Nodes.IndexOf(ReleaseNode);
|
||||
public TrafficNodeDto ReleaseNode { get; set; } = new();
|
||||
public TrafficNodeDto GoalNode { get; set; } = new();
|
||||
public List<TrafficNodeDto> Nodes { get; set; } = [];
|
||||
public List<TrafficEdgeDto> Edges { get; set; } = [];
|
||||
public List<TrafficNodeDto> SubNodes { get; set; } = [];
|
||||
public List<TrafficEdgeDto> SubEdges { get; set; } = [];
|
||||
public List<TrafficNodeDto> GivewayNodes { get; set; } = [];
|
||||
public List<TrafficEdgeDto> GivewayEdges { get; set; } = [];
|
||||
public TrafficSolutionState State { get; set; }
|
||||
public RefreshPathState RefreshPathState { get; set; } = RefreshPathState.Compeleted;
|
||||
public string Message { get; set; } = "";
|
||||
|
||||
private static double GetDistance(List<TrafficNodeDto> nodes)
|
||||
{
|
||||
double distance = 0;
|
||||
for (int i = 0; i < nodes.Count - 1; i++)
|
||||
{
|
||||
distance += Math.Sqrt(Math.Pow(nodes[i].X - nodes[i + 1].X, 2) + Math.Pow(nodes[i].Y - nodes[i + 1].Y, 2));
|
||||
}
|
||||
return distance;
|
||||
}
|
||||
|
||||
public bool Checking(double trafficAvoidableNodeMax, double trafficDistanceMax)
|
||||
{
|
||||
if (State != TrafficSolutionState.GiveWay && State != TrafficSolutionState.LoopResolve &&
|
||||
RefreshPathState != RefreshPathState.Created && RefreshPathState != RefreshPathState.Refreshing)
|
||||
{
|
||||
if (ReleaseNodeIndex != -1 && InNodeIndex <= ReleaseNodeIndex && InNodeIndex < Nodes.Count)
|
||||
{
|
||||
var releaseNodes = Nodes.GetRange(InNodeIndex + 1, ReleaseNodeIndex - InNodeIndex);
|
||||
if (releaseNodes.Where(n => n.IsAvoidableNode).Count() < trafficAvoidableNodeMax) return true;
|
||||
var distance = GetDistance([.. Nodes.GetRange(InNodeIndex, ReleaseNodeIndex - InNodeIndex + 1)]);
|
||||
distance -= Math.Sqrt(Math.Pow(Robot.VisualizationMsg.AgvPosition.X - Nodes[InNodeIndex].X, 2) + Math.Pow(Robot.VisualizationMsg.AgvPosition.Y - Nodes[InNodeIndex].Y, 2));
|
||||
if (distance < trafficDistanceMax) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public TrafficNodeDto[] GetChekingNodes(double trafficAvoidableNodeMax, double trafficDistanceMin)
|
||||
{
|
||||
List<TrafficNodeDto> releaseNodes = [];
|
||||
double distance = 0;
|
||||
distance -= Math.Sqrt(Math.Pow(Nodes[InNodeIndex].X - Robot.VisualizationMsg.AgvPosition.X, 2) + Math.Pow(Nodes[InNodeIndex].Y - Robot.VisualizationMsg.AgvPosition.Y, 2));
|
||||
int index = InNodeIndex < ReleaseNodeIndex ? InNodeIndex + 1 : InNodeIndex;
|
||||
for (; index < Nodes.Count; index++)
|
||||
{
|
||||
releaseNodes.Add(Nodes[index]);
|
||||
if (index < Nodes.Count - 1)
|
||||
{
|
||||
var remainingNodes = Nodes.GetRange(index + 1, Nodes.Count - (index + 1));
|
||||
if (!remainingNodes.Any(n => n.IsAvoidableNode))
|
||||
{
|
||||
index = Nodes.Count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index > 0) distance += Math.Sqrt(Math.Pow(Nodes[index].X - Nodes[index - 1].X, 2) + Math.Pow(Nodes[index].Y - Nodes[index - 1].Y, 2));
|
||||
if (distance < trafficDistanceMin || !Nodes[index].IsAvoidableNode) continue;
|
||||
if (releaseNodes.Where(n => n.IsAvoidableNode).Count() >= trafficAvoidableNodeMax) break;
|
||||
}
|
||||
return index > ReleaseNodeIndex ? [.. Nodes.GetRange(ReleaseNodeIndex, index - ReleaseNodeIndex + (index < Nodes.Count ? 1 : 0))] : [];
|
||||
}
|
||||
|
||||
public (TrafficSolutionState state, string message) UpdateGiveWay(TrafficNodeDto[] giveWayNodes, PathPlanner pathPlanner, MapManager map)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (giveWayNodes.Length < 2) return (TrafficSolutionState.UnableResolve, "Lộ trình tránh đường không hợp lệ");
|
||||
if (!giveWayNodes.Any(n => !Nodes.Contains(n)))
|
||||
{
|
||||
var giveNodeIndex = Nodes.IndexOf(giveWayNodes[^1]);
|
||||
if (giveNodeIndex >= ReleaseNodeIndex)
|
||||
{
|
||||
ReleaseNode = giveWayNodes[^1];
|
||||
State = TrafficSolutionState.Complete;
|
||||
return (TrafficSolutionState.Complete, "");
|
||||
}
|
||||
}
|
||||
var giveWayIndex = Nodes.IndexOf(giveWayNodes[0]);
|
||||
var releaseNodeInGivewaysIndex = Array.FindIndex(giveWayNodes, n=> n.Id == ReleaseNode.Id);
|
||||
if (giveWayIndex != -1)
|
||||
{
|
||||
List<TrafficNodeDto> subGiveWayNodes = [];
|
||||
List<EdgeDto> subGiveWayEdges = [];
|
||||
if (releaseNodeInGivewaysIndex != -1) subGiveWayNodes = giveWayNodes.ToList().GetRange(releaseNodeInGivewaysIndex, giveWayNodes.Length - releaseNodeInGivewaysIndex);
|
||||
else if (giveWayIndex == ReleaseNodeIndex) subGiveWayNodes = [.. giveWayNodes];
|
||||
else if (giveWayIndex < ReleaseNodeIndex)
|
||||
{
|
||||
subGiveWayNodes = Nodes.GetRange(giveWayIndex + 1, ReleaseNodeIndex - giveWayIndex);
|
||||
subGiveWayNodes.Reverse();
|
||||
subGiveWayNodes.AddRange(giveWayNodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
subGiveWayNodes = Nodes.GetRange(ReleaseNodeIndex, giveWayIndex - ReleaseNodeIndex);
|
||||
subGiveWayNodes.AddRange(giveWayNodes);
|
||||
}
|
||||
List<EdgeDto> edges = [..map.GetEdges(MapId, [..subGiveWayNodes.Select(n => new NodeDto
|
||||
{
|
||||
Id = n.Id,
|
||||
Name = n.Name,
|
||||
X = n.X,
|
||||
Y = n.Y,
|
||||
MapId = MapId,
|
||||
})])];
|
||||
if (edges.Count > 0)
|
||||
{
|
||||
double releaseTheta;
|
||||
if (ReleaseNodeIndex > 0)
|
||||
{
|
||||
var (nearNodeX, nearNodeY) = MapEditorHelper.Curve(0.9, new EdgeCaculatorModel
|
||||
{
|
||||
TrajectoryDegree = Edges[ReleaseNodeIndex - 1].TrajectoryDegree,
|
||||
ControlPoint1X = Edges[ReleaseNodeIndex - 1].ControlPoint1X,
|
||||
ControlPoint1Y = Edges[ReleaseNodeIndex - 1].ControlPoint1Y,
|
||||
ControlPoint2X = Edges[ReleaseNodeIndex - 1].ControlPoint2X,
|
||||
ControlPoint2Y = Edges[ReleaseNodeIndex - 1].ControlPoint2Y,
|
||||
X1 = Nodes[ReleaseNodeIndex - 1].X,
|
||||
Y1 = Nodes[ReleaseNodeIndex - 1].Y,
|
||||
X2 = Nodes[ReleaseNodeIndex].X,
|
||||
Y2 = Nodes[ReleaseNodeIndex].Y,
|
||||
});
|
||||
var relaseForward = Math.Atan2(Nodes[ReleaseNodeIndex].Y - nearNodeY, Nodes[ReleaseNodeIndex].X - nearNodeX) * 180 / Math.PI;
|
||||
var releaseBackward = Math.Atan2(nearNodeY - Nodes[ReleaseNodeIndex].Y, nearNodeX - Nodes[ReleaseNodeIndex].X) * 180 / Math.PI;
|
||||
releaseTheta = Nodes[ReleaseNodeIndex - 1].Direction == RobotDirection.FORWARD ? relaseForward : releaseBackward;
|
||||
}
|
||||
else
|
||||
{
|
||||
var (nearNodeX, nearNodeY) = MapEditorHelper.Curve(0.1, new EdgeCaculatorModel
|
||||
{
|
||||
TrajectoryDegree = Edges[ReleaseNodeIndex].TrajectoryDegree,
|
||||
ControlPoint1X = Edges[ReleaseNodeIndex].ControlPoint1X,
|
||||
ControlPoint1Y = Edges[ReleaseNodeIndex].ControlPoint1Y,
|
||||
ControlPoint2X = Edges[ReleaseNodeIndex].ControlPoint2X,
|
||||
ControlPoint2Y = Edges[ReleaseNodeIndex].ControlPoint2Y,
|
||||
X1 = Nodes[ReleaseNodeIndex].X,
|
||||
Y1 = Nodes[ReleaseNodeIndex].Y,
|
||||
X2 = Nodes[ReleaseNodeIndex + 1].X,
|
||||
Y2 = Nodes[ReleaseNodeIndex + 1].Y,
|
||||
});
|
||||
var releaseBackward = Math.Atan2(Nodes[ReleaseNodeIndex].Y - nearNodeY, Nodes[ReleaseNodeIndex].X - nearNodeX) * 180 / Math.PI;
|
||||
var relaseForward = Math.Atan2(nearNodeY - Nodes[ReleaseNodeIndex].Y, nearNodeX - Nodes[ReleaseNodeIndex].X) * 180 / Math.PI;
|
||||
releaseTheta = Nodes[0].Direction == RobotDirection.FORWARD ? relaseForward : releaseBackward;
|
||||
}
|
||||
List<NodeDto> nodes = [..PathPlanner.CalculatorDirection(releaseTheta, [..subGiveWayNodes.Select(n => new NodeDto
|
||||
{
|
||||
Id = n.Id,
|
||||
X = n.X,
|
||||
Y = n.Y,
|
||||
Name = n.Name,
|
||||
MapId = MapId,
|
||||
})], [..edges])];
|
||||
GivewayNodes = [.. nodes.Select(n => new TrafficNodeDto
|
||||
{
|
||||
Id = n.Id,
|
||||
X = n.X,
|
||||
Y = n.Y,
|
||||
Name = n.Name,
|
||||
Direction = MapCompute.GetRobotDirection(n.Direction),
|
||||
})];
|
||||
//Console.WriteLine($"{Robot.SerialNumber} giveway: [{string.Join(",", GivewayNodes.Select(n => $"({n.Name} - {n.Direction})"))}]");
|
||||
foreach (var node in GivewayNodes)
|
||||
{
|
||||
node.IsAvoidableNode = map.GetNegativeNodes(MapId, node.Id).Length > 2;
|
||||
if (node.IsAvoidableNode)
|
||||
{
|
||||
node.AvoidablePaths = [.. map.GetNegativePaths(MapId, new() { Id = node.Id, X = node.X, Y = node.Y, Name = node.Name }, 2)
|
||||
.Where(path => path.Length > 0)
|
||||
.Select(path => path.Select(n => new TrafficNodeDto { Id = n.Id, X = n.X, Y = n.Y, Name = n.Name }).ToArray())];
|
||||
}
|
||||
}
|
||||
GivewayEdges = [.. edges.Select(n => new TrafficEdgeDto
|
||||
{
|
||||
Id = n.Id,
|
||||
ControlPoint1X = n.ControlPoint1X,
|
||||
ControlPoint1Y = n.ControlPoint1Y,
|
||||
ControlPoint2X = n.ControlPoint2X,
|
||||
ControlPoint2Y = n.ControlPoint2Y,
|
||||
StartNodeId = n.StartNodeId,
|
||||
EndNodeId = n.EndNodeId,
|
||||
TrajectoryDegree = n.TrajectoryDegree,
|
||||
})];
|
||||
State = TrafficSolutionState.GiveWay;
|
||||
var angleForward = Math.Atan2(GivewayNodes[^1].Y - GivewayNodes[^2].Y, GivewayNodes[^1].X - GivewayNodes[^2].X) * 180 / Math.PI;
|
||||
var angleBackward = Math.Atan2(GivewayNodes[^2].Y - GivewayNodes[^1].Y, GivewayNodes[^2].X - GivewayNodes[^1].X) * 180 / Math.PI;
|
||||
RefreshPath(GivewayNodes[^1], GivewayNodes[^1].Direction == RobotDirection.FORWARD ? angleForward : angleBackward, pathPlanner, map);
|
||||
return (TrafficSolutionState.GiveWay, "");
|
||||
}
|
||||
return (TrafficSolutionState.UnableResolve, $"Không tìm thấy edge yêu cầu phải tránh [{GivewayNodes[0].Name} - {GivewayNodes[1].Name}]");
|
||||
}
|
||||
return (TrafficSolutionState.UnableResolve, $"Không tìm thấy điểm xung đột {GivewayNodes[0].Name}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (TrafficSolutionState.UnableResolve, $"Cập nhật lộ trình tránh đường xảy ra lỗi: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshPath(TrafficNodeDto currentNode, double theta, PathPlanner planner, MapManager map)
|
||||
{
|
||||
RefreshPathState = RefreshPathState.Created;
|
||||
var plannerTask = Task.Run(async () =>
|
||||
{
|
||||
RefreshPathState = RefreshPathState.Refreshing;
|
||||
var path = await planner.Planning(currentNode.Id, theta, AgentModel.NavigationType, MapId, GoalNode.Id);
|
||||
if (!path.IsSuccess)
|
||||
{
|
||||
RefreshPathState = RefreshPathState.Error;
|
||||
Message = path.Message;
|
||||
return;
|
||||
}
|
||||
if (path.Data.Nodes is null || path.Data.Edges is null || path.Data.Edges.Length == 0 || path.Data.Nodes.Length < 2)
|
||||
{
|
||||
RefreshPathState = RefreshPathState.Error;
|
||||
Message = $"Đường dẫn tới đích [{GoalNode.Name} - {GoalNode.Id}] từ [{currentNode.Name} - {currentNode.Id}] không tồn tại";
|
||||
return;
|
||||
}
|
||||
SubEdges = [..path.Data.Edges.Select(n => new TrafficEdgeDto()
|
||||
{
|
||||
Id = n.Id,
|
||||
StartNodeId = n.StartNodeId,
|
||||
EndNodeId = n.EndNodeId,
|
||||
TrajectoryDegree = n.TrajectoryDegree,
|
||||
ControlPoint1X = n.ControlPoint1X,
|
||||
ControlPoint1Y = n.ControlPoint1Y,
|
||||
ControlPoint2X = n.ControlPoint2X,
|
||||
ControlPoint2Y = n.ControlPoint2Y,
|
||||
})];
|
||||
SubNodes = [..path.Data.Nodes.Select(n => new TrafficNodeDto()
|
||||
{
|
||||
Id = n.Id,
|
||||
Name = n.Name,
|
||||
X = n.X,
|
||||
Y = n.Y,
|
||||
Direction = MapCompute.GetRobotDirection(n.Direction)
|
||||
})];
|
||||
foreach (var node in SubNodes)
|
||||
{
|
||||
node.IsAvoidableNode = map.GetNegativeNodes(MapId, node.Id).Length > 2;
|
||||
if (node.IsAvoidableNode)
|
||||
{
|
||||
node.AvoidablePaths = [.. map.GetNegativePaths(MapId, new() { Id = node.Id, X = node.X, Y = node.Y, Name = node.Name }, 2)
|
||||
.Where(path => path.Length > 0)
|
||||
.Select(path => path.Select(n => new TrafficNodeDto { Id = n.Id, X = n.X, Y = n.Y, Name = n.Name }).ToArray())];
|
||||
}
|
||||
}
|
||||
//Console.WriteLine($"{Robot.SerialNumber} refresh path: [{string.Join(",", SubNodes.Select(n => $"({n.Name} - {n.Direction})"))}]");
|
||||
RefreshPathState = RefreshPathState.Compeleted;
|
||||
});
|
||||
}
|
||||
}
|
||||
13
RobotNet.RobotManager/Services/Traffic/AgentModel.cs
Normal file
13
RobotNet.RobotManager/Services/Traffic/AgentModel.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using RobotNet.RobotShares.Enums;
|
||||
|
||||
namespace RobotNet.RobotManager.Services.Traffic;
|
||||
|
||||
public class AgentModel
|
||||
{
|
||||
public NavigationType NavigationType { get; set; }
|
||||
public double Length { get; set; }
|
||||
public double Width { get; set; }
|
||||
public double NavigationPointX { get; set; }
|
||||
public double NavigationPointY { get; set; }
|
||||
public double TurningRadius { get; set; }
|
||||
}
|
||||
5
RobotNet.RobotManager/Services/Traffic/TrafficAlarm.cs
Normal file
5
RobotNet.RobotManager/Services/Traffic/TrafficAlarm.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace RobotNet.RobotManager.Services.Traffic;
|
||||
|
||||
public class TrafficAlarm
|
||||
{
|
||||
}
|
||||
15
RobotNet.RobotManager/Services/Traffic/TrafficConflict.cs
Normal file
15
RobotNet.RobotManager/Services/Traffic/TrafficConflict.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using RobotNet.RobotShares.Dtos;
|
||||
using RobotNet.RobotShares.Enums;
|
||||
|
||||
namespace RobotNet.RobotManager.Services.Traffic;
|
||||
|
||||
#nullable disable
|
||||
|
||||
public class TrafficConflict
|
||||
{
|
||||
public Agent AgentRequest { get; set; }
|
||||
public Agent AgentConflict { get; set; }
|
||||
public TrafficNodeDto NodeConflict { get; set; }
|
||||
public List<TrafficNodeDto> ReleaseNodes { get; set; }
|
||||
public TrafficConflictState State { get; set; }
|
||||
}
|
||||
20
RobotNet.RobotManager/Services/Traffic/TrafficEdgeDto.cs
Normal file
20
RobotNet.RobotManager/Services/Traffic/TrafficEdgeDto.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using RobotNet.MapShares.Enums;
|
||||
using RobotNet.RobotShares.Dtos;
|
||||
|
||||
namespace RobotNet.RobotManager.Services.Traffic;
|
||||
|
||||
#nullable disable
|
||||
|
||||
public class TrafficEdgeDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid StartNodeId { get; set; }
|
||||
public Guid EndNodeId { get; set; }
|
||||
public TrafficNodeDto StartNode { get; set; }
|
||||
public TrafficNodeDto EndNode { get; set; }
|
||||
public double ControlPoint1X { get; set; }
|
||||
public double ControlPoint1Y { get; set; }
|
||||
public double ControlPoint2X { get; set; }
|
||||
public double ControlPoint2Y { get; set; }
|
||||
public TrajectoryDegree TrajectoryDegree { get; set; }
|
||||
}
|
||||
13
RobotNet.RobotManager/Services/Traffic/TrafficGiveway.cs
Normal file
13
RobotNet.RobotManager/Services/Traffic/TrafficGiveway.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using RobotNet.RobotShares.Dtos;
|
||||
|
||||
namespace RobotNet.RobotManager.Services.Traffic;
|
||||
|
||||
#nullable disable
|
||||
|
||||
public class TrafficGiveway
|
||||
{
|
||||
public string RobotGive { get; set; }
|
||||
public string RobotReceive { get; set; }
|
||||
public TrafficNodeDto[] Nodes { get; set; }
|
||||
public bool IsGiveway { get; set; } = false;
|
||||
}
|
||||
857
RobotNet.RobotManager/Services/Traffic/TrafficManager.cs
Normal file
857
RobotNet.RobotManager/Services/Traffic/TrafficManager.cs
Normal file
@@ -0,0 +1,857 @@
|
||||
using RobotNet.RobotManager.Data;
|
||||
using RobotNet.RobotShares.Dtos;
|
||||
using RobotNet.RobotShares.Enums;
|
||||
using RobotNet.Shares;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace RobotNet.RobotManager.Services.Traffic;
|
||||
|
||||
public class TrafficManager(MapManager Map, IServiceProvider ServiceProvider, PathPlanner PathPlannger, IConfiguration Configuration, LoggerController<TrafficManager> Logger) : IHostedService
|
||||
{
|
||||
private System.Threading.Timer? computeTimer;
|
||||
private const int intervalTime = 1000;
|
||||
|
||||
private readonly double TrafficCheckingDistanceMax = Configuration.GetValue("TrafficConfig:CheckingDistanceMax", 5);
|
||||
private readonly double TrafficCheckingDistanceMin = Configuration.GetValue("TrafficConfig:CheckingDistanceMin", 3);
|
||||
private readonly int TrafficResolutionRepeat = Configuration.GetValue("TrafficConfig:ResolutionRepeat", 1);
|
||||
private readonly int TrafficAvoidableNodeMax = Configuration.GetValue("TrafficConfig:AvoidableNodeMax", 3);
|
||||
private readonly double TrafficOffsetLength = Configuration.GetValue("TrafficConfig:OffsetLength", 0.3);
|
||||
private readonly double TrafficOffsetWidth = Configuration.GetValue("TrafficConfig:OffsetWidth", 0.2);
|
||||
public bool Enable => Configuration.GetValue("TrafficConfig:Enable", false);
|
||||
|
||||
private readonly SemaphoreSlim AgentSemaphore = new(1, 1);
|
||||
private readonly SemaphoreSlim UpdateSemaphore = new(1, 1);
|
||||
|
||||
public ConcurrentDictionary<Guid, TrafficMap> TrafficMaps { get; set; } = [];
|
||||
|
||||
private Agent NewAgent(Guid mapId, IRobotController robotController, AgentModel model, TrafficNodeDto[] nodes, TrafficEdgeDto[] edges)
|
||||
{
|
||||
model.TurningRadius = TrafficMath.CalTurningRadius(model, TrafficOffsetLength, TrafficOffsetWidth);
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
node.AvoidablePaths = [.. Map.GetNegativePaths(mapId, new() { Id = node.Id, X = node.X, Y = node.Y, Name = node.Name }, model.TurningRadius)
|
||||
.Where(path => path.Length > 0)
|
||||
.Select(path => path.Select(n => new TrafficNodeDto { Id = n.Id, X = n.X, Y = n.Y, Name = n.Name }).ToArray())];
|
||||
node.IsAvoidableNode = Map.GetNegativeNodes(mapId, node.Id).Length > 2 && node.AvoidablePaths.Length > 0;
|
||||
if (nodes.Length > 0 && node != nodes.Last() && model.NavigationType != NavigationType.Forklift) node.LockedShapes = new()
|
||||
{
|
||||
Type = TrafficLockedShapeType.Circle,
|
||||
Radius = model.TurningRadius,
|
||||
};
|
||||
else node.LockedShapes = TrafficMath.CalRobotRectShape(model, TrafficOffsetLength, TrafficOffsetWidth, node.X, node.Y, node.Theta);
|
||||
}
|
||||
Agent agent = new()
|
||||
{
|
||||
Robot = robotController,
|
||||
AgentModel = model,
|
||||
Nodes = [.. nodes],
|
||||
InNodeIndex = 0,
|
||||
ReleaseNode = nodes[0],
|
||||
Edges = [.. edges],
|
||||
State = TrafficSolutionState.Complete,
|
||||
MapId = mapId,
|
||||
GoalNode = nodes[^1],
|
||||
};
|
||||
return agent;
|
||||
}
|
||||
|
||||
public MessageResult CreateAgent(Guid mapId, IRobotController robotController, AgentModel model, TrafficNodeDto[] nodes, TrafficEdgeDto[] edges)
|
||||
{
|
||||
if (AgentSemaphore.Wait(2000))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (TrafficMaps.TryGetValue(mapId, out TrafficMap? TrafficMap) && TrafficMap is not null)
|
||||
{
|
||||
if (TrafficMap.Agents.TryGetValue(robotController.SerialNumber, out _)) TrafficMap.Agents.Remove(robotController.SerialNumber);
|
||||
TrafficMap.Agents.Add(robotController.SerialNumber, NewAgent(mapId, robotController, model, nodes, edges));
|
||||
}
|
||||
else
|
||||
{
|
||||
bool result = TrafficMaps.TryAdd(mapId, new TrafficMap()
|
||||
{
|
||||
MapId = mapId,
|
||||
Agents = new Dictionary<string, Agent>()
|
||||
{
|
||||
{
|
||||
robotController.SerialNumber,
|
||||
NewAgent(mapId, robotController, model, nodes, edges)
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) return new(false, $"Không thể tạo TrafficMap cho bản đồ {mapId} do đã tồn tại hoặc xảy ra lỗi khi thêm agent {robotController.SerialNumber}.");
|
||||
}
|
||||
//Logger.Info($"CreateAgent: Tạo Agent thành công - {robotController.SerialNumber} to {nodes[^1].Name}, nodes: [{string.Join(", ", nodes.Select(n => $"[({n.Name}) - ({n.Direction})]"))}]");
|
||||
|
||||
return new(true, "");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning($"Tạo mới traffic Agent xảy ra lỗi - {ex.Message}");
|
||||
return new(false, $"Tạo mới traffic Agent xảy ra lỗi - {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
AgentSemaphore.Release();
|
||||
}
|
||||
}
|
||||
else return new(false, $"Không thể tạo trafficAgent do vượt quá thời gian chờ.");
|
||||
}
|
||||
|
||||
public MessageResult DeleteAgent(string robotId)
|
||||
{
|
||||
if (AgentSemaphore.Wait(2000))
|
||||
{
|
||||
try
|
||||
{
|
||||
using var scope = ServiceProvider.CreateScope();
|
||||
var RobotDb = scope.ServiceProvider.GetRequiredService<RobotEditorDbContext>();
|
||||
var robot = RobotDb.Robots.FirstOrDefault(r => r.RobotId == robotId);
|
||||
if (robot is null) return new(false, $"Không tìm thấy robot Id {robotId} trong kho dữ liệu.");
|
||||
if (TrafficMaps.TryGetValue(robot.MapId, out TrafficMap? TrafficMap) && TrafficMap is not null)
|
||||
{
|
||||
if (TrafficMap.Agents.TryGetValue(robotId, out _))
|
||||
{
|
||||
var remove = TrafficMap.Agents.Remove(robotId);
|
||||
if (!remove) return new(false, $"Không thể xóa agent {robotId}.");
|
||||
}
|
||||
}
|
||||
|
||||
//Logger.Info($"DeleteAgent: Xóa Agent thành công - {robotId}");
|
||||
return new(true, "");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning($"Xóa traffic Agent xảy ra lỗi - {ex.Message}");
|
||||
return new(false, $"Xóa traffic Agent xảy ra lỗi - {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
AgentSemaphore.Release();
|
||||
}
|
||||
}
|
||||
else return new(false, $"Không thể xóa trafficAgent do vượt quá thời gian chờ.");
|
||||
}
|
||||
|
||||
public TrafficSolution GetTrafficNode(string robotId)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var scope = ServiceProvider.CreateScope();
|
||||
var RobotDb = scope.ServiceProvider.GetRequiredService<RobotEditorDbContext>();
|
||||
var robot = RobotDb.Robots.FirstOrDefault(r => r.RobotId == robotId);
|
||||
if (robot is null) return new() { State = TrafficSolutionState.None };
|
||||
|
||||
if (TrafficMaps.TryGetValue(robot.MapId, out TrafficMap? trafficmap) && trafficmap is not null)
|
||||
{
|
||||
if (trafficmap.Agents.TryGetValue(robotId, out Agent? agent) && agent is not null)
|
||||
{
|
||||
TrafficSolution returnSolution = new()
|
||||
{
|
||||
State = agent.State,
|
||||
ReleaseNode = agent.ReleaseNode,
|
||||
GivewayEdges = agent.GivewayEdges,
|
||||
GivewayNodes = agent.GivewayNodes,
|
||||
Nodes = [.. agent.Nodes],
|
||||
Edges = [.. agent.Edges]
|
||||
}; ;
|
||||
switch (agent.State)
|
||||
{
|
||||
case TrafficSolutionState.GiveWay:
|
||||
if (agent.InNodeIndex != agent.ReleaseNodeIndex && (agent.GivewayNodes.Count > 0 && agent.ReleaseNode.Id != agent.GivewayNodes[^1].Id)) returnSolution.State = TrafficSolutionState.Waitting;
|
||||
else
|
||||
{
|
||||
if (agent.GivewayNodes.Count > 0 && agent.ReleaseNode.Id != agent.GivewayNodes[^1].Id) agent.ReleaseNode = agent.GivewayNodes[^1];
|
||||
if (agent.GivewayNodes.Count == 0 && agent.RefreshPathState == RefreshPathState.Compeleted)
|
||||
{
|
||||
agent.State = TrafficSolutionState.RefreshPath;
|
||||
returnSolution.State = TrafficSolutionState.RefreshPath;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TrafficSolutionState.LoopResolve: returnSolution.State = TrafficSolutionState.Complete; break;
|
||||
};
|
||||
// Console.WriteLine($"{robotId} Gettraffic: {returnSolution.State}, release: {returnSolution.ReleaseNode.Name}");
|
||||
return returnSolution;
|
||||
}
|
||||
}
|
||||
return new() { State = TrafficSolutionState.None };
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning($"{robotId} Lấy thông tin traffic xảy ra lỗi: {ex.Message}");
|
||||
return new() { State = TrafficSolutionState.None };
|
||||
}
|
||||
}
|
||||
|
||||
public MessageResult UpdateInNode(string robotId, Guid nodeId)
|
||||
{
|
||||
if (UpdateSemaphore.Wait(2000))
|
||||
{
|
||||
try
|
||||
{
|
||||
using var scope = ServiceProvider.CreateScope();
|
||||
var RobotDb = scope.ServiceProvider.GetRequiredService<RobotEditorDbContext>();
|
||||
var robot = RobotDb.Robots.FirstOrDefault(r => r.RobotId == robotId);
|
||||
if (robot is null) return new(false, $"Không tìm thấy robot Id {robotId} trong kho dữ liệu.");
|
||||
|
||||
if (TrafficMaps.TryGetValue(robot.MapId, out TrafficMap? trafficmap) && trafficmap is not null)
|
||||
{
|
||||
if (trafficmap.Agents.TryGetValue(robotId, out Agent? agent) && agent is not null)
|
||||
{
|
||||
if (agent.State == TrafficSolutionState.GiveWay)
|
||||
{
|
||||
if (agent.GivewayNodes.Count > 1 && nodeId == agent.GivewayNodes[^1].Id)
|
||||
{
|
||||
var giveWayResolution = trafficmap.GivewayResolution.FirstOrDefault(n => n.RobotGive == robotId);
|
||||
if (giveWayResolution is not null)
|
||||
{
|
||||
trafficmap.AddLocker(giveWayResolution.RobotReceive, [.. giveWayResolution.Nodes]);
|
||||
giveWayResolution.IsGiveway = true;
|
||||
}
|
||||
agent.Nodes = agent.SubNodes;
|
||||
agent.Edges = agent.SubEdges;
|
||||
agent.GivewayNodes = [];
|
||||
agent.GivewayEdges = [];
|
||||
}
|
||||
|
||||
List<TrafficNodeDto> lockedNodes = [];
|
||||
var nodeIndex = agent.Nodes.FindIndex(n => n.Id == nodeId);
|
||||
if (nodeIndex != -1)
|
||||
{
|
||||
agent.InNodeIndex = nodeIndex;
|
||||
if (agent.ReleaseNodeIndex != -1 && agent.InNodeIndex <= agent.ReleaseNodeIndex)
|
||||
lockedNodes = agent.Nodes.GetRange(agent.InNodeIndex, agent.ReleaseNodeIndex - agent.InNodeIndex + 1);
|
||||
lockedNodes.AddRange(agent.GivewayNodes.Where(n => !lockedNodes.Any(ln => ln.Id == n.Id)));
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeIndex = agent.GivewayNodes.FindIndex(n => n.Id == nodeId);
|
||||
if (nodeIndex != -1) lockedNodes = agent.GivewayNodes.GetRange(nodeIndex, agent.GivewayNodes.Count - nodeIndex);
|
||||
}
|
||||
|
||||
trafficmap.UpdateLocker(robotId, [.. lockedNodes]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var nodeIndex = agent.Nodes.FindIndex(n => n.Id == nodeId);
|
||||
if (nodeIndex != -1)
|
||||
{
|
||||
agent.InNodeIndex = nodeIndex;
|
||||
|
||||
if (agent.ReleaseNodeIndex != -1 && agent.InNodeIndex <= agent.ReleaseNodeIndex)
|
||||
{
|
||||
var lockedNodes = agent.Nodes.GetRange(agent.InNodeIndex, agent.ReleaseNodeIndex - agent.InNodeIndex + 1);
|
||||
trafficmap.UpdateLocker(robotId, [.. lockedNodes]);
|
||||
trafficmap.GivewayResolution.RemoveAll(s => s.RobotReceive == agent.Robot.SerialNumber && s.IsGiveway && !lockedNodes.Any(n => s.Nodes.Any(sn => sn.Id == n.Id)));
|
||||
}
|
||||
if (agent.State == TrafficSolutionState.LoopResolve && nodeId == agent.ReleaseNode.Id)
|
||||
{
|
||||
var giveWayResolution = trafficmap.GivewayResolution.FirstOrDefault(n => n.RobotGive == robotId);
|
||||
if (giveWayResolution is not null)
|
||||
{
|
||||
trafficmap.AddLocker(giveWayResolution.RobotReceive, [.. giveWayResolution.Nodes]);
|
||||
giveWayResolution.IsGiveway = true;
|
||||
}
|
||||
agent.State = TrafficSolutionState.Waitting;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new(true, "");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning($"{robotId} Cập nhật vị trí xảy ra lỗi: {ex.Message}");
|
||||
return new(false, $"{robotId} Cập nhật vị trí xảy ra lỗi: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
UpdateSemaphore.Release();
|
||||
}
|
||||
}
|
||||
else return new(false, $"Không thể xóa cập nhật dữ liệu vào hệ thống traffic do vượt quá thời gian chờ.");
|
||||
}
|
||||
|
||||
private static (TrafficConflictState state, string robotId, TrafficNodeDto? nodeId) FirstCheckingReleaseNodes(TrafficNodeDto[] releaseNodes, Agent agent, TrafficMap trafficMap)
|
||||
{
|
||||
if (releaseNodes.Length != 0)
|
||||
{
|
||||
foreach (var releaseNode in releaseNodes)
|
||||
{
|
||||
foreach (var locked in trafficMap.Locked)
|
||||
{
|
||||
if (locked.Key == agent.Robot.SerialNumber) continue;
|
||||
if (locked.Value.Any(n => n.Id == releaseNode.Id)) return (TrafficConflictState.Vertex, locked.Key, releaseNode);
|
||||
//foreach (var lockedNode in locked.Value)
|
||||
//{
|
||||
// var distance = Math.Sqrt(Math.Pow(lockedNode.X - releaseNode.X, 2) + Math.Pow(lockedNode.Y - releaseNode.Y, 2));
|
||||
// if(distance > (lockedNode.LockedShapes.Radius + releaseNode.LockedShapes.Radius)) continue;
|
||||
// if (lockedNode.LockedShapes.Type == TrafficLockedShapeType.Circle)
|
||||
// {
|
||||
// if (releaseNode.LockedShapes.Type == TrafficLockedShapeType.Circle)
|
||||
// {
|
||||
// if (distance < (lockedNode.LockedShapes.Radius + releaseNode.LockedShapes.Radius)) return (TrafficConflictState.Proximity, locked.Key, releaseNode);
|
||||
// }
|
||||
// else if (releaseNode.LockedShapes.Type == TrafficLockedShapeType.Rectangle)
|
||||
// {
|
||||
// if(TrafficMath.RectIntersectCircle(lockedNode, lockedNode.LockedShapes.Radius,
|
||||
// releaseNode.LockedShapes.X1, releaseNode.LockedShapes.Y1,
|
||||
// releaseNode.LockedShapes.X2, releaseNode.LockedShapes.Y2,
|
||||
// releaseNode.LockedShapes.X3, releaseNode.LockedShapes.Y3,
|
||||
// releaseNode.LockedShapes.X4, releaseNode.LockedShapes.Y4)) return (TrafficConflictState.Proximity, locked.Key, releaseNode);
|
||||
// }
|
||||
// }
|
||||
// else if (lockedNode.LockedShapes.Type == TrafficLockedShapeType.Rectangle)
|
||||
// {
|
||||
// if (releaseNode.LockedShapes.Type == TrafficLockedShapeType.Circle)
|
||||
// {
|
||||
// if(TrafficMath.RectIntersectRect(lockedNode.LockedShapes, releaseNode.LockedShapes)) return (TrafficConflictState.Proximity, locked.Key, releaseNode);
|
||||
// }
|
||||
// else if (releaseNode.LockedShapes.Type == TrafficLockedShapeType.Circle)
|
||||
// {
|
||||
// if (TrafficMath.RectIntersectCircle(releaseNode, releaseNode.LockedShapes.Radius,
|
||||
// lockedNode.LockedShapes.X1, lockedNode.LockedShapes.Y1,
|
||||
// lockedNode.LockedShapes.X2, lockedNode.LockedShapes.Y2,
|
||||
// lockedNode.LockedShapes.X3, lockedNode.LockedShapes.Y3,
|
||||
// lockedNode.LockedShapes.X4, lockedNode.LockedShapes.Y4)) return (TrafficConflictState.Proximity, locked.Key, releaseNode);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (TrafficConflictState.None, string.Empty, null);
|
||||
}
|
||||
|
||||
private static (bool IsSuccess, string robotId, TrafficNodeDto? nodeId) GivewayCheckingReleaseNodes(TrafficNodeDto[] releaseNodes, string robotId, TrafficMap trafficMap)
|
||||
{
|
||||
foreach (var node in releaseNodes)
|
||||
{
|
||||
foreach (var locked in trafficMap.Locked)
|
||||
{
|
||||
if (locked.Value.Any(n => (n.Id == node.Id)) && locked.Key != robotId)
|
||||
return (false, locked.Key, node);
|
||||
}
|
||||
}
|
||||
return (true, string.Empty, null);
|
||||
}
|
||||
|
||||
private void CheckConflict(Agent agent, TrafficMap trafficMap)
|
||||
{
|
||||
if (agent.Checking(TrafficAvoidableNodeMax, TrafficCheckingDistanceMax) && !trafficMap.GivewayResolution.Any(s => s.RobotGive == agent.Robot.SerialNumber))
|
||||
{
|
||||
var releaseNodes = agent.GetChekingNodes(TrafficAvoidableNodeMax, TrafficCheckingDistanceMin);
|
||||
if (releaseNodes.Length > 0)
|
||||
{
|
||||
(var state, string conflictRobotId, TrafficNodeDto? conflictNode) = FirstCheckingReleaseNodes(releaseNodes, agent, trafficMap);
|
||||
if (state == TrafficConflictState.None)
|
||||
{
|
||||
|
||||
trafficMap.AddLocker(agent.Robot.SerialNumber, [.. releaseNodes]);
|
||||
agent.ReleaseNode = releaseNodes[^1];
|
||||
agent.State = TrafficSolutionState.Complete;
|
||||
trafficMap.Conflicts.RemoveAll(conflict => conflict.AgentRequest.Robot.SerialNumber == agent.Robot.SerialNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (conflictNode is null)
|
||||
{
|
||||
Logger.Warning($"{agent.Robot.SerialNumber} kiểm tra xung đột có lỗi: xảy ra xung đột nhưng không tìm thấy node xung đột");
|
||||
return;
|
||||
}
|
||||
|
||||
if (trafficMap.Agents.TryGetValue(conflictRobotId, out Agent? conflictAgent) && conflictAgent is not null)
|
||||
{
|
||||
trafficMap.Conflicts.RemoveAll(c => c.AgentRequest.Robot.SerialNumber == agent.Robot.SerialNumber);
|
||||
var conflictState = GetConflictState(agent, conflictAgent, conflictNode);
|
||||
if (conflictState == TrafficConflictState.Confrontation || state == TrafficConflictState.Proximity)
|
||||
{
|
||||
Level1ConflictResolution(agent, conflictAgent, [.. releaseNodes], conflictNode, trafficMap);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
trafficMap.Conflicts.Add(new()
|
||||
{
|
||||
AgentConflict = conflictAgent,
|
||||
AgentRequest = agent,
|
||||
NodeConflict = conflictNode,
|
||||
ReleaseNodes = [.. releaseNodes],
|
||||
State = TrafficConflictState.Vertex,
|
||||
});
|
||||
agent.State = TrafficSolutionState.Waitting;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// cần xử lí khi bị xung đột với robot đang không di chuyển
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static TrafficConflictState GetConflictState(Agent agent1, Agent agent2, TrafficNodeDto conflictNode)
|
||||
{
|
||||
var conflictNodeAgent2Index = agent2.Nodes.IndexOf(conflictNode);
|
||||
if (conflictNodeAgent2Index != -1)
|
||||
{
|
||||
var remainingNodes = agent2.Nodes.GetRange(conflictNodeAgent2Index, agent2.Nodes.Count - conflictNodeAgent2Index);
|
||||
if (remainingNodes.Any(n => n.Id == agent1.ReleaseNode.Id)) return TrafficConflictState.Confrontation;
|
||||
return TrafficConflictState.Vertex;
|
||||
}
|
||||
|
||||
return TrafficConflictState.None;
|
||||
}
|
||||
|
||||
private TrafficSolutionState Level1ConflictResolution(Agent agent1, Agent agent2, List<TrafficNodeDto> releaseNodes, TrafficNodeDto conflictNode, TrafficMap trafficMap)
|
||||
{
|
||||
try
|
||||
{
|
||||
var remainingNodes = agent2.Nodes.GetRange(agent2.InNodeIndex, agent2.Nodes.Count - agent2.InNodeIndex);
|
||||
var conflictIndex = releaseNodes.IndexOf(conflictNode);
|
||||
if (conflictIndex != -1)
|
||||
{
|
||||
//double distance = 0;
|
||||
for (int i = conflictIndex - 1; i >= 0; i--)
|
||||
{
|
||||
//distance += Math.Sqrt(Math.Pow(releaseNodes[i].X - releaseNodes[i + 1].X, 2) + Math.Pow(releaseNodes[i].Y - releaseNodes[i + 1].Y, 2));
|
||||
//if (distance < TrafficLockedCircle) continue;
|
||||
if (!remainingNodes.Any(n => n.Id == releaseNodes[i].Id))
|
||||
{
|
||||
var givewayNodes = releaseNodes.GetRange(0, releaseNodes.Count - i);
|
||||
(var IsSuccess, string conflictRobotId, TrafficNodeDto? _) = GivewayCheckingReleaseNodes([.. givewayNodes], agent1.Robot.SerialNumber, trafficMap);
|
||||
if (IsSuccess)
|
||||
{
|
||||
trafficMap.AddLocker(agent1.Robot.SerialNumber, [.. givewayNodes]);
|
||||
agent1.State = TrafficSolutionState.Complete;
|
||||
agent1.ReleaseNode = givewayNodes[^1];
|
||||
trafficMap.Conflicts.RemoveAll(conflict => conflict.AgentRequest.Robot.SerialNumber == agent1.Robot.SerialNumber);
|
||||
return TrafficSolutionState.Complete;
|
||||
}
|
||||
}
|
||||
if (!releaseNodes[i].IsAvoidableNode) continue;
|
||||
|
||||
var state = Level2ConflictResolution(agent1, agent2, [.. releaseNodes[i].AvoidablePaths], [.. releaseNodes], [.. releaseNodes.GetRange(0, i + 1)], trafficMap);
|
||||
if (state != TrafficSolutionState.UnableResolve) return state;
|
||||
}
|
||||
if (!trafficMap.GivewayResolution.Any(s => s.RobotReceive == agent1.Robot.SerialNumber))
|
||||
{
|
||||
trafficMap.Conflicts.Add(new()
|
||||
{
|
||||
AgentRequest = agent1,
|
||||
AgentConflict = agent2,
|
||||
NodeConflict = conflictNode,
|
||||
ReleaseNodes = releaseNodes,
|
||||
State = TrafficConflictState.Confrontation,
|
||||
});
|
||||
}
|
||||
return TrafficSolutionState.UnableResolve;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning($"{agent1.Robot.SerialNumber} tìm kiếm giải pháp tránh xung đột bậc 1 xảy ra lỗi: confilct node không được tìm thấy trong release node {conflictNode.Name}, release list [{string.Join(", ", releaseNodes.Select(n => n.Name))}]");
|
||||
}
|
||||
|
||||
return TrafficSolutionState.UnableResolve;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning($"{agent1.Robot.SerialNumber} tìm kiếm giải pháp tránh xung đột bậc 1 xảy ra lỗi: {ex.Message}");
|
||||
return TrafficSolutionState.UnableResolve;
|
||||
}
|
||||
}
|
||||
|
||||
private TrafficSolutionState Level2ConflictResolution(Agent agent1, Agent agent2, TrafficNodeDto[][] avoidablePaths, TrafficNodeDto[] relaseNodes, TrafficNodeDto[] conflictNodes, TrafficMap trafficMap)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (agent2.State == TrafficSolutionState.GiveWay) return TrafficSolutionState.Waitting;
|
||||
List<(Agent conflictAgent, TrafficNodeDto conflictNode)> bufferConflict = [];
|
||||
var remainingNodeAgent2 = agent2.Nodes.GetRange(agent2.InNodeIndex, agent2.Nodes.Count - agent2.InNodeIndex);
|
||||
var remainingReleaseNodeAgent2 = agent2.Nodes.GetRange(agent2.InNodeIndex, agent2.ReleaseNodeIndex - agent2.InNodeIndex + 1);
|
||||
foreach (var avoidablePath in avoidablePaths)
|
||||
{
|
||||
if (avoidablePath.Any(avoidableNode => remainingReleaseNodeAgent2.Any(n => n.Id == avoidableNode.Id)) ||
|
||||
remainingNodeAgent2.Any(avoidableNode => avoidableNode.Id == avoidablePath[^1].Id)) continue;
|
||||
|
||||
(var IsSuccess, string conflictRobotId, TrafficNodeDto? conflictNode) = GivewayCheckingReleaseNodes([.. conflictNodes, .. avoidablePath], agent1.Robot.SerialNumber, trafficMap);
|
||||
if (IsSuccess)
|
||||
{
|
||||
TrafficNodeDto[] givewayNodes = [.. conflictNodes, .. avoidablePath.Skip(1)];
|
||||
trafficMap.AddLocker(agent1.Robot.SerialNumber, givewayNodes);
|
||||
var (state, message) = agent1.UpdateGiveWay(givewayNodes, PathPlannger, Map);
|
||||
Logger.Info($"{agent1.Robot.SerialNumber} tránh Robot {agent2.Robot.SerialNumber} tại Node {givewayNodes[^1].Name} {(state == TrafficSolutionState.Complete || state == TrafficSolutionState.GiveWay ? "thành công" : $"xảy ra lỗi: {message}")}");
|
||||
if (state == TrafficSolutionState.GiveWay)
|
||||
trafficMap.GivewayResolution.Add(new()
|
||||
{
|
||||
RobotGive = agent1.Robot.SerialNumber,
|
||||
RobotReceive = agent2.Robot.SerialNumber,
|
||||
Nodes = [conflictNodes[^1]]
|
||||
});
|
||||
return state;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (conflictNode is null)
|
||||
{
|
||||
Logger.Warning($"{agent1.Robot.SerialNumber} tìm kiếm giải pháp tránh xung đột bậc 2 có lỗi: xảy ra xung đột nhưng không tìm thấy node xung đột");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (trafficMap.Agents.TryGetValue(conflictRobotId, out Agent? conflictAgent) && conflictAgent is not null)
|
||||
bufferConflict.Add((conflictAgent, conflictNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (conflictAgent, conflictNode) in bufferConflict)
|
||||
{
|
||||
var trafficState = Level3ConflictResolution(conflictAgent, agent1.Robot.SerialNumber, conflictNode, relaseNodes, trafficMap, 0);
|
||||
if (trafficState == TrafficSolutionState.Complete || trafficState == TrafficSolutionState.GiveWay) return trafficState;
|
||||
}
|
||||
|
||||
//return FindAvoidableResolution(agent1, agent2, trafficMap) ;
|
||||
return TrafficSolutionState.UnableResolve;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning($"{agent1.Robot.SerialNumber} tìm kiếm giải pháp tránh xung đột bậc 2 xảy ra lỗi: {ex.Message}");
|
||||
return TrafficSolutionState.UnableResolve;
|
||||
}
|
||||
}
|
||||
|
||||
private TrafficSolutionState Level3ConflictResolution(Agent agent, string giveWayRobotId, TrafficNodeDto giveNode, TrafficNodeDto[] relaseNodes, TrafficMap trafficMap, int counter)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (agent.State == TrafficSolutionState.GiveWay || agent.State == TrafficSolutionState.LoopResolve) return TrafficSolutionState.Waitting;
|
||||
List<(Agent conflictAgent, TrafficNodeDto conflictNode)> bufferConflict = [];
|
||||
|
||||
var negativePaths = Map.GetNegativePaths(trafficMap.MapId, new() { Id = giveNode.Id, X = giveNode.X, Y = giveNode.Y, Name = giveNode.Name }, agent.AgentModel.TurningRadius)
|
||||
.Where(path => path.Length > 0)
|
||||
.Select(path => path.Select(n => new TrafficNodeDto { Id = n.Id, X = n.X, Y = n.Y, Name = n.Name }).ToArray())
|
||||
.ToList();
|
||||
|
||||
foreach (var negativePath in negativePaths)
|
||||
{
|
||||
if (negativePath.Any(negativeNode => relaseNodes.Any(n => n.Id == negativeNode.Id) && negativeNode.Id != giveNode.Id)) continue;
|
||||
|
||||
(var IsSuccess, string conflictRobotId, TrafficNodeDto? conflictNode) = GivewayCheckingReleaseNodes(negativePath, agent.Robot.SerialNumber, trafficMap);
|
||||
if (IsSuccess)
|
||||
{
|
||||
trafficMap.AddLocker(agent.Robot.SerialNumber, negativePath);
|
||||
var (state, message) = agent.UpdateGiveWay(negativePath, PathPlannger, Map);
|
||||
Logger.Info($"{agent.Robot.SerialNumber} tránh level3 Robot {giveWayRobotId} tại Node {negativePath[^1].Name} {(state == TrafficSolutionState.Complete || state == TrafficSolutionState.GiveWay ? "thành công" : $"xảy ra lỗi: {message}")}");
|
||||
if (state == TrafficSolutionState.GiveWay)
|
||||
trafficMap.GivewayResolution.Add(new()
|
||||
{
|
||||
RobotGive = agent.Robot.SerialNumber,
|
||||
RobotReceive = giveWayRobotId,
|
||||
Nodes = [giveNode]
|
||||
});
|
||||
return state;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (conflictNode is null)
|
||||
{
|
||||
Logger.Warning($"{agent.Robot.SerialNumber} tìm kiếm giải pháp tránh xung đột bậc 3 có lỗi: xảy ra xung đột nhưng không tìm thấy node xung đột");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (trafficMap.Agents.TryGetValue(conflictRobotId, out Agent? conflictAgent) && conflictAgent is not null)
|
||||
bufferConflict.Add((conflictAgent, conflictNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (counter < TrafficResolutionRepeat)
|
||||
{
|
||||
foreach (var (conflictAgent, conflictNode) in bufferConflict)
|
||||
{
|
||||
var trafficState = Level3ConflictResolution(conflictAgent, agent.Robot.SerialNumber, conflictNode, [.. relaseNodes, giveNode], trafficMap, ++counter);
|
||||
if (trafficState == TrafficSolutionState.Complete || trafficState == TrafficSolutionState.GiveWay) return trafficState;
|
||||
}
|
||||
}
|
||||
return TrafficSolutionState.UnableResolve;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning($"{agent.Robot.SerialNumber} tìm kiếm giải pháp tránh xung đột bậc 3 xảy ra lỗi: {ex.Message}");
|
||||
return TrafficSolutionState.UnableResolve;
|
||||
}
|
||||
}
|
||||
|
||||
private TrafficSolutionState FindAvoidableResolution(Agent agent1, Agent agent2, TrafficMap trafficMap)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (agent2.State == TrafficSolutionState.GiveWay || agent2.State == TrafficSolutionState.LoopResolve) return TrafficSolutionState.Waitting;
|
||||
if (agent2.ReleaseNodeIndex == -1 || agent1.ReleaseNodeIndex == -1) return TrafficSolutionState.Waitting;
|
||||
var conflictNodes = agent2.Nodes.GetRange(agent2.ReleaseNodeIndex, agent2.Nodes.Count - agent2.ReleaseNodeIndex);
|
||||
List<(Agent conflictAgent, TrafficNodeDto conflictNode)> bufferConflict = [];
|
||||
for (int i = 1; i < conflictNodes.Count; i++)
|
||||
{
|
||||
if (conflictNodes[i].IsAvoidableNode)
|
||||
{
|
||||
var remainingNodeAgent2 = agent2.Nodes.GetRange(agent2.ReleaseNodeIndex, agent2.Nodes.Count - agent2.ReleaseNodeIndex);
|
||||
foreach (var avoidablePath in conflictNodes[i].AvoidablePaths)
|
||||
{
|
||||
if (remainingNodeAgent2.Any(n => n.Id == avoidablePath[^1].Id)) continue;
|
||||
var givewayagent2 = conflictNodes.GetRange(1, i);
|
||||
var mergeNode = givewayagent2.LastOrDefault(n => agent1.Nodes.Any(a1 => a1.Id == n.Id));
|
||||
if (mergeNode is not null)
|
||||
{
|
||||
var avoidableIndex = givewayagent2.FindIndex(n => n.Id == mergeNode.Id);
|
||||
if (avoidableIndex != -1)
|
||||
{
|
||||
var giveWayNodes = givewayagent2.GetRange(avoidableIndex, givewayagent2.Count - avoidableIndex - 1);
|
||||
giveWayNodes.AddRange(avoidablePath);
|
||||
(var IsSuccess, string conflictRobotId, TrafficNodeDto? conflictNode) = GivewayCheckingReleaseNodes([.. giveWayNodes], agent1.Robot.SerialNumber, trafficMap);
|
||||
if (IsSuccess)
|
||||
{
|
||||
trafficMap.AddLocker(agent1.Robot.SerialNumber, [.. giveWayNodes]);
|
||||
var (state, message) = agent1.UpdateGiveWay([.. giveWayNodes], PathPlannger, Map);
|
||||
Logger.Info($"{agent1.Robot.SerialNumber} giải quyết đối đầu với robot {agent2.Robot.SerialNumber} tại node {giveWayNodes[^1].Name} {(state == TrafficSolutionState.Complete || state == TrafficSolutionState.GiveWay ? "thành công" : $"xảy ra lỗi: {message}")}");
|
||||
if (state == TrafficSolutionState.GiveWay)
|
||||
trafficMap.GivewayResolution.Add(new()
|
||||
{
|
||||
RobotGive = agent1.Robot.SerialNumber,
|
||||
RobotReceive = agent2.Robot.SerialNumber,
|
||||
Nodes = [conflictNodes[i]]
|
||||
});
|
||||
return state;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (conflictNode is null)
|
||||
{
|
||||
Logger.Warning($"{agent1.Robot.SerialNumber} tìm kiếm giải pháp tránh xung đột đối đầu có lỗi: xảy ra xung đột nhưng không tìm thấy node xung đột");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (trafficMap.Agents.TryGetValue(conflictRobotId, out Agent? conflictAgent) && conflictAgent is not null)
|
||||
bufferConflict.Add((conflictAgent, conflictNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var (conflictAgent, conflictNode) in bufferConflict)
|
||||
{
|
||||
var trafficState = Level3ConflictResolution(conflictAgent, agent1.Robot.SerialNumber, conflictNode, [.. conflictNodes], trafficMap, 0);
|
||||
if (trafficState == TrafficSolutionState.Complete || trafficState == TrafficSolutionState.GiveWay) return trafficState;
|
||||
}
|
||||
|
||||
return TrafficSolutionState.UnableResolve;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning($"{agent1.Robot.SerialNumber} tìm kiếm giải pháp tránh xung đột đối đầu xảy ra lỗi: {ex.Message}");
|
||||
return TrafficSolutionState.UnableResolve;
|
||||
}
|
||||
}
|
||||
|
||||
private TrafficSolutionState LoopConflictResolution(TrafficConflict[] conflicts, TrafficMap trafficMap)
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < conflicts.Length; i++)
|
||||
{
|
||||
var conflictAvoid = conflicts.FirstOrDefault(c => c.AgentConflict.Robot.SerialNumber == conflicts[i].AgentRequest.Robot.SerialNumber);
|
||||
if (conflictAvoid is null) continue;
|
||||
var conflictResolutionState = LoopConflictLevel1Resolution(conflicts[i], conflictAvoid, trafficMap);
|
||||
if (conflictResolutionState == TrafficSolutionState.Complete) return conflictResolutionState;
|
||||
}
|
||||
for (int i = 0; i < conflicts.Length; i++)
|
||||
{
|
||||
var conflictAvoid = conflicts.FirstOrDefault(c => c.AgentConflict.Robot.SerialNumber == conflicts[i].AgentRequest.Robot.SerialNumber);
|
||||
if (conflictAvoid is null) continue;
|
||||
var conflictResolutionState = LoopConflictLevel2Resolution(conflicts[i], conflictAvoid, trafficMap);
|
||||
if (conflictResolutionState == TrafficSolutionState.Complete) return conflictResolutionState;
|
||||
}
|
||||
return TrafficSolutionState.UnableResolve;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning($"LoopConflictResolution: Hệ thống xảy ra lỗi - {ex.Message}");
|
||||
return TrafficSolutionState.UnableResolve;
|
||||
}
|
||||
}
|
||||
|
||||
private TrafficSolutionState LoopConflictLevel1Resolution(TrafficConflict conflict1, TrafficConflict conflict2, TrafficMap trafficMap)
|
||||
{
|
||||
try
|
||||
{
|
||||
var agent1ConflictIndex = conflict1.ReleaseNodes.IndexOf(conflict1.NodeConflict);
|
||||
var agent2ConflictIndex = conflict2.ReleaseNodes.IndexOf(conflict2.NodeConflict);
|
||||
if (agent1ConflictIndex == -1 || agent2ConflictIndex == -1) return TrafficSolutionState.UnableResolve;
|
||||
var agent1ConflictNodes = conflict1.ReleaseNodes.GetRange(0, agent1ConflictIndex);
|
||||
var agent2ConflictNodes = conflict2.ReleaseNodes.GetRange(0, agent2ConflictIndex + 1);
|
||||
|
||||
Logger.Info($"{conflict1.AgentRequest.Robot.SerialNumber} xung đột lặp lại với robot {conflict2.AgentRequest.Robot.SerialNumber} - release 1: [{conflict1.ReleaseNodes[0].Name} => {conflict1.ReleaseNodes[^1].Name} - {conflict1.NodeConflict.Name}] - release 2: [{conflict2.ReleaseNodes[0].Name} => {conflict2.ReleaseNodes[^1].Name} - {conflict2.NodeConflict.Name}]");
|
||||
|
||||
var avoidableNode = agent1ConflictNodes.FirstOrDefault(n => !agent2ConflictNodes.Any(n2 => n2.Id == n.Id));
|
||||
if (avoidableNode is not null)
|
||||
{
|
||||
TrafficNodeDto[] releaseNodes = [.. conflict1.ReleaseNodes.GetRange(0, conflict1.ReleaseNodes.IndexOf(avoidableNode) + 1)];
|
||||
(var conflictState, string conflictRobotId, TrafficNodeDto? conflictNode) = FirstCheckingReleaseNodes(releaseNodes, conflict1.AgentRequest, trafficMap);
|
||||
if (conflictState == TrafficConflictState.None)
|
||||
{
|
||||
Logger.Info($"{conflict1.AgentRequest.Robot.SerialNumber} giải quyết xung đột lặp lại với robot {conflict2.AgentRequest.Robot.SerialNumber} tại node {avoidableNode.Name} - release {releaseNodes[^1].Name} ");
|
||||
trafficMap.AddLocker(conflict1.AgentRequest.Robot.SerialNumber, [.. releaseNodes]);
|
||||
conflict1.AgentRequest.ReleaseNode = releaseNodes[^1];
|
||||
conflict1.AgentRequest.State = TrafficSolutionState.LoopResolve;
|
||||
trafficMap.GivewayResolution.Add(new()
|
||||
{
|
||||
RobotGive = conflict1.AgentRequest.Robot.SerialNumber,
|
||||
RobotReceive = conflict2.AgentRequest.Robot.SerialNumber,
|
||||
Nodes = [.. agent2ConflictNodes]
|
||||
});
|
||||
return TrafficSolutionState.Complete;
|
||||
}
|
||||
}
|
||||
return TrafficSolutionState.Waitting;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning($"LoopConflictLevel1Resolution: Hệ thống xảy ra lỗi - {ex.Message}");
|
||||
return TrafficSolutionState.UnableResolve;
|
||||
}
|
||||
}
|
||||
|
||||
private TrafficSolutionState LoopConflictLevel2Resolution(TrafficConflict conflict1, TrafficConflict conflict2, TrafficMap trafficMap)
|
||||
{
|
||||
try
|
||||
{
|
||||
var agent2ConflictIndex = conflict2.ReleaseNodes.IndexOf(conflict2.NodeConflict);
|
||||
if (agent2ConflictIndex == -1) return TrafficSolutionState.UnableResolve;
|
||||
var agent2ConflictNodes = conflict2.ReleaseNodes.GetRange(0, agent2ConflictIndex + 1);
|
||||
|
||||
Logger.Info($"{conflict1.AgentRequest.Robot.SerialNumber} xung đột lặp lại với robot {conflict2.AgentRequest.Robot.SerialNumber} - release 1: [{conflict1.ReleaseNodes[0].Name} => {conflict1.ReleaseNodes[^1].Name} - {conflict1.NodeConflict.Name}] - release 2: [{conflict2.ReleaseNodes[0].Name} => {conflict2.ReleaseNodes[^1].Name} - {conflict2.NodeConflict.Name}]");
|
||||
|
||||
var avoidableNode = conflict1.ReleaseNodes.FirstOrDefault(n => !agent2ConflictNodes.Any(n2 => n2.Id == n.Id));
|
||||
if (avoidableNode is not null)
|
||||
{
|
||||
TrafficNodeDto[] releaseNodes = [.. conflict1.ReleaseNodes.GetRange(0, conflict1.ReleaseNodes.IndexOf(avoidableNode) + 1)];
|
||||
(var conflictState, string conflictRobotId, TrafficNodeDto? conflictNode) = FirstCheckingReleaseNodes(releaseNodes, conflict1.AgentRequest, trafficMap);
|
||||
if (conflictState == TrafficConflictState.None)
|
||||
{
|
||||
Logger.Info($"{conflict1.AgentRequest.Robot.SerialNumber} giải quyết xung đột lặp lại với robot {conflict2.AgentRequest.Robot.SerialNumber} tại node {avoidableNode.Name} - release {releaseNodes[^1].Name} ");
|
||||
trafficMap.AddLocker(conflict1.AgentRequest.Robot.SerialNumber, [.. releaseNodes]);
|
||||
conflict1.AgentRequest.ReleaseNode = releaseNodes[^1];
|
||||
conflict1.AgentRequest.State = TrafficSolutionState.LoopResolve;
|
||||
trafficMap.GivewayResolution.Add(new()
|
||||
{
|
||||
RobotGive = conflict1.AgentRequest.Robot.SerialNumber,
|
||||
RobotReceive = conflict2.AgentRequest.Robot.SerialNumber,
|
||||
Nodes = [.. agent2ConflictNodes]
|
||||
});
|
||||
return TrafficSolutionState.Complete;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (trafficMap.Agents.TryGetValue(conflictRobotId, out Agent? conflictAgent) && conflictAgent is not null)
|
||||
{
|
||||
var state = Level3ConflictResolution(conflictAgent, conflict1.AgentRequest.Robot.SerialNumber, avoidableNode, [.. releaseNodes, .. agent2ConflictNodes], trafficMap, 0);
|
||||
if (state == TrafficSolutionState.Complete || state == TrafficSolutionState.GiveWay)
|
||||
{
|
||||
trafficMap.Conflicts.RemoveAll(c => c.AgentRequest.Robot.SerialNumber == conflictAgent.Robot.SerialNumber);
|
||||
return TrafficSolutionState.Complete;
|
||||
}
|
||||
return TrafficSolutionState.UnableResolve;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TrafficSolutionState.Waitting;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning($"LoopConflictLevel2Resolution: Hệ thống xảy ra lỗi - {ex.Message}");
|
||||
return TrafficSolutionState.UnableResolve;
|
||||
}
|
||||
}
|
||||
|
||||
private void ComputeHandler(object? state)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var trafficmap in TrafficMaps)
|
||||
{
|
||||
var agents = trafficmap.Value.Agents.Values.ToList();
|
||||
foreach (var agent in agents)
|
||||
{
|
||||
CheckConflict(agent, trafficmap.Value);
|
||||
}
|
||||
var conflicts = trafficmap.Value.Conflicts.ToList();
|
||||
for (int i = 0; i < conflicts.Count; i++)
|
||||
{
|
||||
int repeat = 0;
|
||||
bool isProccessed = false;
|
||||
string agentConflict = conflicts[i].AgentConflict.Robot.SerialNumber;
|
||||
List<TrafficConflict> loopConflicts = [conflicts[i]];
|
||||
while (repeat++ <= TrafficResolutionRepeat)
|
||||
{
|
||||
var loopConflict = conflicts.FirstOrDefault(cl => cl.AgentRequest.Robot.SerialNumber == agentConflict);
|
||||
if (loopConflict is null) break;
|
||||
loopConflicts.Add(loopConflict);
|
||||
if (loopConflict.AgentConflict.Robot.SerialNumber == conflicts[i].AgentRequest.Robot.SerialNumber)
|
||||
{
|
||||
if (loopConflicts.Count > 2)
|
||||
{
|
||||
var resolutionstate = LoopConflictResolution([.. loopConflicts], trafficmap.Value);
|
||||
isProccessed = resolutionstate == TrafficSolutionState.Complete;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else agentConflict = loopConflict.AgentConflict.Robot.SerialNumber;
|
||||
}
|
||||
if (isProccessed) break;
|
||||
|
||||
if (conflicts[i].State == TrafficConflictState.Confrontation)
|
||||
{
|
||||
if (conflicts.Any(c => c.State == TrafficConflictState.Confrontation && c.AgentRequest.Robot.SerialNumber == conflicts[i].AgentConflict.Robot.SerialNumber))
|
||||
{
|
||||
var resolutionstate = FindAvoidableResolution(conflicts[i].AgentRequest, conflicts[i].AgentConflict, trafficmap.Value);
|
||||
if (resolutionstate == TrafficSolutionState.Complete || resolutionstate == TrafficSolutionState.GiveWay)
|
||||
{
|
||||
trafficmap.Value.Conflicts.Remove(conflicts[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
computeTimer?.Change(intervalTime, 0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning($"TrafficCompute: Hệ thống xảy ra lỗi - {ex.Message}");
|
||||
computeTimer?.Change(intervalTime, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (!Enable) return;
|
||||
computeTimer = new Timer(ComputeHandler, null, Timeout.Infinite, Timeout.Infinite);
|
||||
computeTimer.Change(intervalTime, 0);
|
||||
await Task.Yield();
|
||||
}
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (computeTimer != null)
|
||||
{
|
||||
computeTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
await computeTimer.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
26
RobotNet.RobotManager/Services/Traffic/TrafficMap.cs
Normal file
26
RobotNet.RobotManager/Services/Traffic/TrafficMap.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using RobotNet.RobotShares.Dtos;
|
||||
|
||||
namespace RobotNet.RobotManager.Services.Traffic;
|
||||
|
||||
public class TrafficMap
|
||||
{
|
||||
public Guid MapId { get; set; }
|
||||
public Dictionary<string, Agent> Agents { get; set; } = [];
|
||||
public Dictionary<string, List<TrafficNodeDto>> Locked { get; set; } = [];
|
||||
public List<TrafficConflict> Conflicts { get; set; } = [];
|
||||
public List<TrafficGiveway> GivewayResolution { get; set; } = [];
|
||||
|
||||
public void AddLocker(string robotId, TrafficNodeDto[] releaseNodes)
|
||||
{
|
||||
//Console.WriteLine($"{robotId} Add Locker: {string.Join(", ", releaseNodes.Select(n => n.Name))}");
|
||||
if (Locked.TryGetValue(robotId, out List<TrafficNodeDto>? lockedNodes) && lockedNodes is not null) Locked[robotId].AddRange(releaseNodes.Where(n => !lockedNodes.Any(ln => ln.Id == n.Id)));
|
||||
else Locked.Add(robotId, [.. releaseNodes]);
|
||||
}
|
||||
|
||||
public void UpdateLocker(string robotId, TrafficNodeDto[] releaseNodes)
|
||||
{
|
||||
//Console.WriteLine($"{robotId} Update Locker: {string.Join(", ", releaseNodes.Select(n => n.Name))}");
|
||||
if (Locked.TryGetValue(robotId, out _)) Locked[robotId] = [.. releaseNodes];
|
||||
else Locked.Add(robotId, [.. releaseNodes]);
|
||||
}
|
||||
}
|
||||
158
RobotNet.RobotManager/Services/Traffic/TrafficMath.cs
Normal file
158
RobotNet.RobotManager/Services/Traffic/TrafficMath.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
using RobotNet.MapShares;
|
||||
using RobotNet.MapShares.Dtos;
|
||||
using RobotNet.RobotShares.Dtos;
|
||||
|
||||
namespace RobotNet.RobotManager.Services.Traffic;
|
||||
|
||||
public class TrafficMath
|
||||
{
|
||||
public static bool Edge1IntersectEdge(TrafficNodeDto centerCircle, double radius, EdgeCaculatorModel edge)
|
||||
{
|
||||
if (edge.TrajectoryDegree != MapShares.Enums.TrajectoryDegree.One) return true;
|
||||
|
||||
double dx = edge.X2 - edge.X1;
|
||||
double dy = edge.Y2 - edge.Y1;
|
||||
var length = Math.Sqrt(Math.Pow(dx, 2) + Math.Pow(dy, 2));
|
||||
if (length == 0) return Math.Sqrt(Math.Pow(edge.X1 - centerCircle.X, 2) + Math.Pow(edge.Y1 - centerCircle.Y, 2)) <= radius;
|
||||
|
||||
double distance = Math.Abs(dy * centerCircle.X - dx * centerCircle.Y + (edge.X2 * edge.Y1 - edge.X1 * edge.Y2)) / length;
|
||||
if (distance > radius) return false;
|
||||
|
||||
double a = dx * dx + dy * dy;
|
||||
double b = 2 * (dx * (edge.X1 - centerCircle.X) + dy * (edge.Y1 - centerCircle.Y));
|
||||
double c = (edge.X1 - centerCircle.X) * (edge.X1 - centerCircle.X) + (edge.Y1 - centerCircle.Y) * (edge.Y1 - centerCircle.Y) - radius * radius;
|
||||
|
||||
double delta = b * b - 4 * a * c;
|
||||
if (delta < 0) return false;
|
||||
|
||||
double t1 = (-b + Math.Sqrt(delta)) / (2 * a);
|
||||
double t2 = (-b - Math.Sqrt(delta)) / (2 * a);
|
||||
|
||||
return (t1 >= 0 && t1 <= 1) || (t2 >= 0 && t2 <= 1);
|
||||
}
|
||||
|
||||
public static bool Edge2IntersectEdge(TrafficNodeDto centerCircle, double radius, EdgeCaculatorModel edge)
|
||||
{
|
||||
if (edge.TrajectoryDegree != MapShares.Enums.TrajectoryDegree.Two) return true;
|
||||
|
||||
var length = Math.Sqrt(Math.Pow(edge.X2 - edge.X1, 2) + Math.Pow(edge.Y2 - edge.Y1, 2));
|
||||
if (length == 0) return Math.Sqrt(Math.Pow(edge.X1 - centerCircle.X, 2) + Math.Pow(edge.Y1 - centerCircle.Y, 2)) <= radius;
|
||||
|
||||
double step = 0.1 / length;
|
||||
for (double t = 0; t <= 1.001; t += step)
|
||||
{
|
||||
(double x1, double y1) = CurveDegreeTwo(t, edge.X1, edge.Y1, edge.ControlPoint1X, edge.ControlPoint1Y, edge.X2, edge.Y2);
|
||||
if (Math.Sqrt(Math.Pow(x1 - centerCircle.X, 2) + Math.Pow(y1 - centerCircle.Y, 2)) < radius) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool Edge3IntersectEdge(TrafficNodeDto centerCircle, double radius, EdgeCaculatorModel edge)
|
||||
{
|
||||
if (edge.TrajectoryDegree != MapShares.Enums.TrajectoryDegree.Three) return true;
|
||||
|
||||
var length = Math.Sqrt(Math.Pow(edge.X2 - edge.X1, 2) + Math.Pow(edge.Y2 - edge.Y1, 2));
|
||||
if (length == 0) return Math.Sqrt(Math.Pow(edge.X1 - centerCircle.X, 2) + Math.Pow(edge.Y1 - centerCircle.Y, 2)) <= radius;
|
||||
|
||||
double step = 0.1 / length;
|
||||
for (double t = 0; t <= 1.001; t += step)
|
||||
{
|
||||
(double x1, double y1) = CurveDegreeThree(t, edge.X1, edge.Y1, edge.ControlPoint1X, edge.ControlPoint1Y, edge.ControlPoint2X, edge.ControlPoint2Y, edge.X2, edge.Y2);
|
||||
if (Math.Sqrt(Math.Pow(x1 - centerCircle.X, 2) + Math.Pow(y1 - centerCircle.Y, 2)) < radius) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool EdgeIntersectCircle(TrafficNodeDto centerCircle, double radius, EdgeCaculatorModel edge)
|
||||
{
|
||||
if (edge.TrajectoryDegree == MapShares.Enums.TrajectoryDegree.One) return Edge1IntersectEdge(centerCircle, radius, edge);
|
||||
else if (edge.TrajectoryDegree == MapShares.Enums.TrajectoryDegree.Two) return Edge2IntersectEdge(centerCircle, radius, edge);
|
||||
else if (edge.TrajectoryDegree == MapShares.Enums.TrajectoryDegree.Three) return Edge3IntersectEdge(centerCircle, radius, edge);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static (double x, double y) CurveDegreeTwo(double t, double x1, double y1, double controlPointX, double controlPointY, double x2, double y2)
|
||||
{
|
||||
var x = (1 - t) * (1 - t) * x1 + 2 * t * (1 - t) * controlPointX + t * t * x2;
|
||||
var y = (1 - t) * (1 - t) * y1 + 2 * t * (1 - t) * controlPointY + t * t * y2;
|
||||
return (x, y);
|
||||
}
|
||||
|
||||
public static (double x, double y) CurveDegreeThree(double t, double x1, double y1, double controlPoint1X, double controlPoint1Y, double controlPoint2X, double controlPoint2Y, double x2, double y2)
|
||||
{
|
||||
var x = Math.Pow(1 - t, 3) * x1 + 3 * Math.Pow(1 - t, 2) * t * controlPoint1X + 3 * Math.Pow(t, 2) * (1 - t) * controlPoint2X + Math.Pow(t, 3) * x2; ;
|
||||
var y = Math.Pow(1 - t, 3) * y1 + 3 * Math.Pow(1 - t, 2) * t * controlPoint1Y + 3 * Math.Pow(t, 2) * (1 - t) * controlPoint2Y + Math.Pow(t, 3) * y2;
|
||||
return (x, y);
|
||||
}
|
||||
|
||||
public static double CalTurningRadius(AgentModel model, double offsetLength, double offsetWidth)
|
||||
{
|
||||
var navigationPointX = offsetLength - model.NavigationPointX;
|
||||
var navigationPointY = offsetWidth - model.NavigationPointY;
|
||||
var length = model.Length + 2 * offsetLength;
|
||||
var width = model.Width + 2 * offsetWidth;
|
||||
double d1 = Math.Sqrt(navigationPointX * navigationPointX + navigationPointY * navigationPointY);
|
||||
double d2 = Math.Sqrt((length - navigationPointX) * (length - navigationPointX) + navigationPointY * navigationPointY);
|
||||
double d3 = Math.Sqrt(navigationPointX * navigationPointX + (width - navigationPointY) * (width - navigationPointY));
|
||||
double d4 = Math.Sqrt((length - navigationPointX) * (length - navigationPointX) + (width - navigationPointY) * (width - navigationPointY));
|
||||
|
||||
return Math.Max(Math.Max(d1, d2), Math.Max(d3, d4));
|
||||
}
|
||||
|
||||
public static TrafficLockedShapeDto CalRobotRectShape(AgentModel model, double offsetLength, double offsetWidth, double x, double y, double theta)
|
||||
{
|
||||
double x1 = model.NavigationPointX - offsetLength;
|
||||
double y1 = model.NavigationPointY - offsetWidth;
|
||||
double x2 = model.Length + model.NavigationPointX + offsetLength;
|
||||
double y2 = model.NavigationPointY - offsetWidth;
|
||||
double x3 = model.Length + model.NavigationPointX + offsetLength;
|
||||
double y3 = model.Width + model.NavigationPointY + offsetWidth;
|
||||
double x4 = model.NavigationPointX - offsetLength;
|
||||
double y4 = model.Width + model.NavigationPointY + offsetWidth;
|
||||
|
||||
return new()
|
||||
{
|
||||
Type = TrafficLockedShapeType.Rectangle,
|
||||
X1 = x1 * Math.Cos(theta) - y1 * Math.Sin(theta) + x,
|
||||
Y1 = x1 * Math.Sin(theta) + y1 * Math.Cos(theta) + y,
|
||||
X2 = x2 + Math.Cos(theta) - y2 * Math.Sin(theta) + x,
|
||||
Y2 = x2 * Math.Sin(theta) + y2 * Math.Cos(theta) + y,
|
||||
X3 = x3 + Math.Cos(theta) - y3 * Math.Sin(theta) + x,
|
||||
Y3 = x3 * Math.Sin(theta) + y3 * Math.Cos(theta) + y,
|
||||
X4 = x4 + Math.Cos(theta) - y4 * Math.Sin(theta) + x,
|
||||
Y4 = x4 * Math.Sin(theta) + y4 * Math.Cos(theta) + y
|
||||
};
|
||||
}
|
||||
|
||||
public static bool RectIntersectCircle(TrafficNodeDto centerCircle, double radius, double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
|
||||
{
|
||||
double xMin = Math.Min(Math.Min(x1, x2), Math.Min(x3, x4));
|
||||
double xMax = Math.Max(Math.Max(x1, x2), Math.Max(x3, x4));
|
||||
double yMin = Math.Min(Math.Min(y1, y2), Math.Min(y3, y4));
|
||||
double yMax = Math.Max(Math.Max(y1, y2), Math.Max(y3, y4));
|
||||
|
||||
double xClosest = Math.Max(xMin, Math.Min(centerCircle.X, xMax));
|
||||
double yClosest = Math.Max(yMin, Math.Min(centerCircle.Y, yMax));
|
||||
|
||||
double distance = Math.Sqrt((centerCircle.X - xClosest) * (centerCircle.X - xClosest) + (centerCircle.Y - yClosest) * (centerCircle.Y - yClosest));
|
||||
return distance <= radius;
|
||||
}
|
||||
|
||||
public static bool RectIntersectRect(TrafficLockedShapeDto rect1, TrafficLockedShapeDto rect2)
|
||||
{
|
||||
double x1Min = Math.Min(Math.Min(rect1.X1, rect1.X2), Math.Min(rect1.X3, rect1.X4));
|
||||
double x1Max = Math.Max(Math.Max(rect1.X1, rect1.X2), Math.Max(rect1.X3, rect1.X4));
|
||||
double y1Min = Math.Min(Math.Min(rect1.Y1, rect1.Y2), Math.Min(rect1.Y3, rect1.Y4));
|
||||
double y1Max = Math.Max(Math.Max(rect1.Y1, rect1.Y2), Math.Max(rect1.Y3, rect1.Y4));
|
||||
|
||||
double x2Min = Math.Min(Math.Min(rect2.X1, rect2.X2), Math.Min(rect2.X3, rect2.X4));
|
||||
double x2Max = Math.Max(Math.Max(rect2.X1, rect2.X2), Math.Max(rect2.X3, rect2.X4));
|
||||
double y2Min = Math.Min(Math.Min(rect2.Y1, rect2.Y2), Math.Min(rect2.Y3, rect2.Y4));
|
||||
double y2Max = Math.Max(Math.Max(rect2.Y1, rect2.Y2), Math.Max(rect2.Y3, rect2.Y4));
|
||||
|
||||
bool xOverlap = x1Min <= x2Max && x2Min <= x1Max;
|
||||
bool yOverlap = y1Min <= y2Max && y2Min <= y1Max;
|
||||
|
||||
return xOverlap && yOverlap;
|
||||
}
|
||||
}
|
||||
84
RobotNet.RobotManager/Services/Traffic/TrafficPublisher.cs
Normal file
84
RobotNet.RobotManager/Services/Traffic/TrafficPublisher.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using RobotNet.RobotManager.Hubs;
|
||||
using RobotNet.RobotShares.Dtos;
|
||||
|
||||
namespace RobotNet.RobotManager.Services.Traffic;
|
||||
|
||||
public class TrafficPublisher(TrafficManager TrafficManager, IHubContext<TrafficHub> TrafficHub, LoggerController<TrafficPublisher> Logger) : IHostedService
|
||||
{
|
||||
private const int intervalTime = 2000;
|
||||
private WatchTimer<TrafficPublisher>? Timer;
|
||||
|
||||
public readonly Dictionary<Guid, string> TrafficMapActive = [];
|
||||
|
||||
public List<TrafficAgentDto> GetAgents(Guid MapId)
|
||||
{
|
||||
if (TrafficManager.TrafficMaps.TryGetValue(MapId, out TrafficMap? trafficMap))
|
||||
{
|
||||
List<TrafficAgentDto> agents = [];
|
||||
List<TrafficNodeDto> lockedNodes = [];
|
||||
foreach (var agent in trafficMap.Agents)
|
||||
{
|
||||
if (trafficMap.Locked.TryGetValue(agent.Key, out List<TrafficNodeDto>? locked) && locked is not null) lockedNodes = locked;
|
||||
var trafficConflict = trafficMap.Conflicts.FirstOrDefault(c => c.AgentRequest.Robot.SerialNumber == agent.Key);
|
||||
agents.Add(new()
|
||||
{
|
||||
RobotId = agent.Key,
|
||||
State = agent.Value.State,
|
||||
ReleaseNode = agent.Value.ReleaseNode,
|
||||
Nodes = agent.Value.Nodes,
|
||||
InNode = agent.Value.Nodes[agent.Value.InNodeIndex],
|
||||
LockedNodes = lockedNodes,
|
||||
SubNodes = agent.Value.SubNodes,
|
||||
GiveWayNodes = agent.Value.GivewayNodes,
|
||||
ConflictAgentId = trafficConflict is null ? "" : trafficConflict.AgentConflict.Robot.SerialNumber,
|
||||
ConflictNode = trafficConflict?.NodeConflict,
|
||||
});
|
||||
}
|
||||
foreach (var agent in trafficMap.Locked)
|
||||
{
|
||||
if (agents.Any(a => a.RobotId == agent.Key)) continue;
|
||||
agents.Add(new()
|
||||
{
|
||||
RobotId = agent.Key,
|
||||
LockedNodes = agent.Value,
|
||||
});
|
||||
}
|
||||
return agents;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private void TimerHandler()
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var mapActive in TrafficMapActive)
|
||||
{
|
||||
var agents = GetAgents(mapActive.Key);
|
||||
if (agents.Count > 0)
|
||||
{
|
||||
var pub = Task.Run(async () => await TrafficHub.Clients.Client(mapActive.Value).SendAsync("TrafficAgentUpdated", agents));
|
||||
pub.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Robot Publisher Error: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Timer = new(intervalTime, TimerHandler, Logger);
|
||||
Timer.Start();
|
||||
await Task.Yield();
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Timer?.Dispose();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
14
RobotNet.RobotManager/Services/Traffic/TrafficSolution.cs
Normal file
14
RobotNet.RobotManager/Services/Traffic/TrafficSolution.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using RobotNet.RobotShares.Dtos;
|
||||
using RobotNet.RobotShares.Enums;
|
||||
|
||||
namespace RobotNet.RobotManager.Services.Traffic;
|
||||
|
||||
public class TrafficSolution
|
||||
{
|
||||
public TrafficSolutionState State { get; set; }
|
||||
public TrafficNodeDto[] Nodes { get; set; } = [];
|
||||
public TrafficEdgeDto[] Edges { get; set; } = [];
|
||||
public TrafficNodeDto ReleaseNode { get; set; } = new();
|
||||
public List<TrafficNodeDto> GivewayNodes { get; set; } = [];
|
||||
public List<TrafficEdgeDto> GivewayEdges { get; set; } = [];
|
||||
}
|
||||
Reference in New Issue
Block a user