This commit is contained in:
chi
2025-05-26 17:42:24 +09:00
parent d6e7c118fb
commit 9f4bec4142
2 changed files with 111 additions and 143 deletions

View File

@@ -14,13 +14,38 @@ using AGVControl.Models;
namespace AGVControl
{
public class RFIDConnection
{
public uint StartRFID { get; set; }
public uint EndRFID { get; set; }
public bool IsBidirectional { get; set; }
public float Distance { get; set; }
public List<uint> IntermediateRFIDs { get; set; } = new List<uint>();
public override bool Equals(object obj)
{
if (obj is RFIDConnection other)
{
return (StartRFID == other.StartRFID && EndRFID == other.EndRFID) ||
(IsBidirectional && other.IsBidirectional &&
StartRFID == other.EndRFID && EndRFID == other.StartRFID);
}
return false;
}
public override int GetHashCode()
{
return StartRFID.GetHashCode() ^ EndRFID.GetHashCode();
}
}
public partial class MapControl : Control
{
private List<RFIDPoint> rfidPoints;
private List<MagnetLine> magnetLines;
private List<MapText> mapTexts;
private List<CustomLine> customLines;
private List<RFIDLine> rfidLines;
private HashSet<RFIDConnection> rfidConnections;
public AGV agv;
private float zoom = 1.0f;
private PointF offset = PointF.Empty;
@@ -41,14 +66,12 @@ namespace AGVControl
private bool isAddingText = false;
private bool isAddingPoint = false;
private bool isDrawingCustomLine = false;
private bool isDrawingMagnetLine = false;
private bool isDrawingRFIDLine = false;
private bool isDeletingRFIDLine = false;
private bool isDrawingLine = false;
private MapText selectedText = null;
private CustomLine selectedLine = null;
private RFIDPoint selectedRFID = null;
private MagnetLine selectedMagnetLine = null;
private RFIDLine selectedRFIDLine = null;
private Point? draggingPoint = null;
private bool isDraggingPoint = false;
@@ -61,7 +84,6 @@ namespace AGVControl
public MapText SelectedText => selectedText;
public CustomLine SelectedLine => selectedLine;
public RFIDPoint SelectedRFID => selectedRFID;
public MagnetLine SelectedMagnetLine => selectedMagnetLine;
public RFIDLine SelectedRFIDLine => selectedRFIDLine;
@@ -77,10 +99,10 @@ namespace AGVControl
{
this.DoubleBuffered = true;
rfidPoints = new List<RFIDPoint>();
magnetLines = new List<MagnetLine>();
mapTexts = new List<MapText>();
customLines = new List<CustomLine>();
rfidLines = new List<RFIDLine>();
rfidConnections = new HashSet<RFIDConnection>();
agv = new AGV();
// 툴바 버튼 영역 초기화
@@ -177,19 +199,6 @@ namespace AGVControl
}
}
// 마그넷 라인의 끝점과 근접한지 확인
foreach (var line in magnetLines)
{
if (GetDistance(point, line.StartPoint) <= SNAP_DISTANCE)
{
return line.StartPoint;
}
if (GetDistance(point, line.EndPoint) <= SNAP_DISTANCE)
{
return line.EndPoint;
}
}
return point;
}
@@ -245,7 +254,7 @@ namespace AGVControl
isDragging = true;
this.Cursor = Cursors.SizeAll;
}
else if (e.Button == MouseButtons.Left && !isAddingText && !isDrawingCustomLine && !isDrawingRFIDLine && !isDrawingMagnetLine)
else if (e.Button == MouseButtons.Left && !isAddingText && !isDrawingCustomLine && !isDrawingRFIDLine)
{
isDragging = true;
@@ -822,9 +831,6 @@ namespace AGVControl
};
rfidLines.Add(line);
selectedRFIDLine = line;
// 선 근처의 RFID 포인트들을 찾아서 추가
AddNearbyRFIDPoints(line);
}
// 다음 라인을 위해 현재 클릭한 RFID를 시작점으로 설정
previewStartPoint = clickedRFID.Location;
@@ -967,12 +973,6 @@ namespace AGVControl
this.Invalidate();
}
public void SetMagnetLines(List<MagnetLine> lines)
{
magnetLines = lines;
this.Invalidate();
}
public void SetAGV(AGV vehicle)
{
agv = vehicle;
@@ -1076,7 +1076,6 @@ namespace AGVControl
{
isDrawingCustomLine = false;
isDrawingLine = false;
isDrawingMagnetLine = false;
isDrawingRFIDLine = false;
isAddingPoint = value;
this.Cursor = value ? Cursors.Cross : Cursors.Default;
@@ -1142,25 +1141,12 @@ namespace AGVControl
}
}
if (selectedMagnetLine != null)
if (selectedRFIDLine != null)
{
using (Pen pen = new Pen(Color.Magenta, 2))
{
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
e.Graphics.DrawLine(pen, selectedMagnetLine.StartPoint, selectedMagnetLine.EndPoint);
foreach (var branch in selectedMagnetLine.BranchPoints)
{
e.Graphics.DrawEllipse(pen, branch.X - 5, branch.Y - 5, 10, 10);
}
}
}
if (selectedLine != null)
{
using (Pen pen = new Pen(Color.Magenta, 2))
{
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
e.Graphics.DrawLine(pen, selectedLine.StartPoint, selectedLine.EndPoint);
e.Graphics.DrawLine(pen, selectedRFIDLine.StartPoint, selectedRFIDLine.EndPoint);
}
}
@@ -1185,23 +1171,6 @@ namespace AGVControl
private void DrawMap(Graphics g)
{
//// 마그넷 라인 그리기
//foreach (var line in magnetLines)
//{
// using (Pen linePen = new Pen(Color.FromArgb(180, Color.Yellow), LINE_WIDTH))
// {
// g.DrawLine(linePen, line.StartPoint, line.EndPoint);
// }
// // 분기점 그리기
// foreach (var branch in line.BranchPoints)
// {
// g.FillEllipse(Brushes.Red, branch.X - 3, branch.Y - 3, 6, 6);
// var direction = line.BranchDirections[branch];
// g.DrawString(direction.ToString(), Font, Brushes.Black, branch.X + 5, branch.Y - 5);
// }
//}
// RFID 포인트 그리기
foreach (var rfid in rfidPoints)
{
@@ -1297,20 +1266,23 @@ namespace AGVControl
private void DrawRFIDLines(Graphics g)
{
foreach (var line in rfidLines)
foreach (var connection in rfidConnections)
{
var startPoint = rfidPoints.First(p => p.RFIDValue == connection.StartRFID).Location;
var endPoint = rfidPoints.First(p => p.RFIDValue == connection.EndRFID).Location;
using (Pen linePen = new Pen(Color.FromArgb(50, Color.Wheat), 2))
{
if (line.IsBidirectional)
if (connection.IsBidirectional)
{
// 단방향 화살표 그리기
var arrowSize = 10;
var angle = Math.Atan2(line.EndPoint.Y - line.StartPoint.Y, line.EndPoint.X - line.StartPoint.X);
var angle = Math.Atan2(endPoint.Y - startPoint.Y, endPoint.X - startPoint.X);
var arrowPoint = new PointF(
line.EndPoint.X - (float)(arrowSize * Math.Cos(angle)),
line.EndPoint.Y - (float)(arrowSize * Math.Sin(angle))
endPoint.X - (float)(arrowSize * Math.Cos(angle)),
endPoint.Y - (float)(arrowSize * Math.Sin(angle))
);
g.DrawLine(linePen, line.StartPoint, arrowPoint);
g.DrawLine(linePen, startPoint, arrowPoint);
// 화살표 머리 그리기
var arrowAngle = Math.PI / 6;
@@ -1328,7 +1300,7 @@ namespace AGVControl
}
else
{
g.DrawLine(linePen, line.StartPoint, line.EndPoint);
g.DrawLine(linePen, startPoint, endPoint);
}
}
}
@@ -1368,21 +1340,6 @@ namespace AGVControl
return rfidPoints;
}
public List<MagnetLine> GetMagnetLines()
{
return magnetLines;
}
public List<MapText> GetMapTexts()
{
return mapTexts;
}
public List<CustomLine> GetCustomLines()
{
return customLines;
}
public List<RFIDLine> GetRFIDLines()
{
return rfidLines;
@@ -1466,7 +1423,6 @@ namespace AGVControl
public void LoadMapData(MapData mapData)
{
rfidPoints = mapData.RFIDPoints ?? new List<RFIDPoint>();
magnetLines = mapData.MagnetLines ?? new List<MagnetLine>();
mapTexts = mapData.MapTexts ?? new List<MapText>();
customLines = mapData.CustomLines ?? new List<CustomLine>();
rfidLines = mapData.RFIDLines ?? new List<RFIDLine>();
@@ -1476,7 +1432,6 @@ namespace AGVControl
public void ClearMap()
{
rfidPoints.Clear();
magnetLines.Clear();
mapTexts.Clear();
customLines.Clear();
rfidLines.Clear();
@@ -1485,7 +1440,6 @@ namespace AGVControl
selectedText = null;
selectedLine = null;
selectedRFID = null;
selectedMagnetLine = null;
selectedRFIDLine = null;
draggingPoint = null;
@@ -1509,25 +1463,6 @@ namespace AGVControl
this.Invalidate();
}
public void AddMagnetLine(Point startPoint, Point endPoint, Point? branchPoint = null, BranchDirection? branchDirection = null)
{
// 이미 맵 좌표로 변환된 위치를 받아서 사용
var line = new MagnetLine
{
StartPoint = startPoint,
EndPoint = endPoint
};
if (branchPoint.HasValue && branchDirection.HasValue)
{
line.BranchPoints.Add(branchPoint.Value);
line.BranchDirections[branchPoint.Value] = branchDirection.Value;
}
magnetLines.Add(line);
this.Invalidate();
}
public void SaveToFile(string filename)
{
var lines = new List<string>();
@@ -1541,10 +1476,12 @@ namespace AGVControl
// RFID 라인 저장
lines.Add("[RFID_LINES]");
foreach (var line in rfidLines)
foreach (var connection in rfidConnections)
{
lines.Add($"{line.StartPoint.X},{line.StartPoint.Y},{line.EndPoint.X},{line.EndPoint.Y}," +
$"{line.StartRFID},{line.EndRFID},{line.IsBidirectional},{line.Distance}");
var startPoint = rfidPoints.First(p => p.RFIDValue == connection.StartRFID).Location;
var endPoint = rfidPoints.First(p => p.RFIDValue == connection.EndRFID).Location;
lines.Add($"{startPoint.X},{startPoint.Y},{endPoint.X},{endPoint.Y}," +
$"{connection.StartRFID},{connection.EndRFID},{connection.IsBidirectional},{connection.Distance}");
}
// 텍스트 저장
@@ -1561,7 +1498,6 @@ namespace AGVControl
lines.Add($"{line.StartPoint.X},{line.StartPoint.Y},{line.EndPoint.X},{line.EndPoint.Y},{line.LineColor.ToArgb()},{line.LineWidth}");
}
File.WriteAllLines(filename, lines);
}
@@ -1614,35 +1550,6 @@ namespace AGVControl
}
break;
case "[MAGNET_LINES]":
var lineParts = line.Split('|');
var mainParts = lineParts[0].Split(',');
if (mainParts.Length >= 4)
{
var magnetLine = new MagnetLine
{
StartPoint = new Point(int.Parse(mainParts[0]), int.Parse(mainParts[1])),
EndPoint = new Point(int.Parse(mainParts[2]), int.Parse(mainParts[3]))
};
// 분기점 정보 처리
for (int i = 1; i < lineParts.Length; i++)
{
var branchParts = lineParts[i].Split(',');
if (branchParts.Length >= 3)
{
var branchPoint = new Point(
int.Parse(branchParts[0]),
int.Parse(branchParts[1])
);
magnetLine.BranchPoints.Add(branchPoint);
magnetLine.BranchDirections[branchPoint] = (BranchDirection)int.Parse(branchParts[2]);
}
}
magnetLines.Add(magnetLine);
}
break;
case "[MAP_TEXTS]":
var textParts = line.Split(',');
if (textParts.Length >= 7)
@@ -1673,16 +1580,79 @@ namespace AGVControl
customLines.Add(customLine);
}
break;
}
}
// RFID 연결 정보 처리
ProcessRFIDConnections();
this.Invalidate();
return sb.ToString();
}
private void ProcessRFIDConnections()
{
rfidConnections.Clear();
var connectionSet = new HashSet<string>();
foreach (var line in rfidLines)
{
var start = line.StartPoint;
var end = line.EndPoint;
// 1. 선 위의 모든 RFID 포인트(시작, 끝 포함)를 projectionRatio로 정렬
var pointsOnThisLine = rfidPoints
.Where(p => IsPointOnLine(p.Location, start, end, 15f)) // 오차 허용치 넉넉히
.Select(p => new
{
RFID = p.RFIDValue,
Ratio = GetProjectionRatio(p.Location, start, end)
})
.ToList();
// 2. 시작/끝 RFID가 목록에 없으면 강제로 추가
if (!pointsOnThisLine.Any(p => p.RFID == line.StartRFID))
pointsOnThisLine.Add(new { RFID = line.StartRFID, Ratio = 0f });
if (!pointsOnThisLine.Any(p => p.RFID == line.EndRFID))
pointsOnThisLine.Add(new { RFID = line.EndRFID, Ratio = 1f });
// 3. 정렬
pointsOnThisLine = pointsOnThisLine.OrderBy(p => p.Ratio).ToList();
// 4. 순서대로 1:1 연결
for (int i = 0; i < pointsOnThisLine.Count - 1; i++)
{
var from = pointsOnThisLine[i].RFID;
var to = pointsOnThisLine[i + 1].RFID;
var key = $"{Math.Min(from, to)}_{Math.Max(from, to)}";
if (connectionSet.Contains(key)) continue;
var fromPt = rfidPoints.FirstOrDefault(p => p.RFIDValue == from)?.Location ?? line.StartPoint;
var toPt = rfidPoints.FirstOrDefault(p => p.RFIDValue == to)?.Location ?? line.EndPoint;
rfidConnections.Add(new RFIDConnection
{
StartRFID = from,
EndRFID = to,
IsBidirectional = line.IsBidirectional,
Distance = GetDistance(fromPt, toPt)
});
connectionSet.Add(key);
}
}
}
// tolerance 인자를 받는 IsPointOnLine
private bool IsPointOnLine(Point point, Point lineStart, Point lineEnd, float tolerance = 10.0f)
{
var distance = GetDistanceToLine(point, lineStart, lineEnd);
if (distance > tolerance) return false;
var projectionRatio = GetProjectionRatio(point, lineStart, lineEnd);
return projectionRatio >= 0 && projectionRatio <= 1;
}
private void DeleteNearbyRFIDLine(Point clickPoint)
{
const float DELETE_DISTANCE = 10.0f; // 클릭 지점으로부터의 허용 거리

View File

@@ -57,9 +57,7 @@
<Compile Include="GuideSensor.Designer.cs">
<DependentUpon>GuideSensor.cs</DependentUpon>
</Compile>
<Compile Include="MapControl.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="MapControl.cs" />
<Compile Include="MapControl.Designer.cs">
<DependentUpon>MapControl.cs</DependentUpon>
</Compile>