diff --git a/Cs_HMI/SubProject/AGVControl/MapControl.cs b/Cs_HMI/SubProject/AGVControl/MapControl.cs index 6ee69bd..94370b9 100644 --- a/Cs_HMI/SubProject/AGVControl/MapControl.cs +++ b/Cs_HMI/SubProject/AGVControl/MapControl.cs @@ -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 IntermediateRFIDs { get; set; } = new List(); + + 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 rfidPoints; - private List magnetLines; private List mapTexts; private List customLines; private List rfidLines; + private HashSet 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(); - magnetLines = new List(); mapTexts = new List(); customLines = new List(); rfidLines = new List(); + rfidConnections = new HashSet(); 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 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 GetMagnetLines() - { - return magnetLines; - } - - public List GetMapTexts() - { - return mapTexts; - } - - public List GetCustomLines() - { - return customLines; - } - public List GetRFIDLines() { return rfidLines; @@ -1466,7 +1423,6 @@ namespace AGVControl public void LoadMapData(MapData mapData) { rfidPoints = mapData.RFIDPoints ?? new List(); - magnetLines = mapData.MagnetLines ?? new List(); mapTexts = mapData.MapTexts ?? new List(); customLines = mapData.CustomLines ?? new List(); rfidLines = mapData.RFIDLines ?? new List(); @@ -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(); @@ -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(); + + 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; // 클릭 지점으로부터의 허용 거리 diff --git a/Cs_HMI/SubProject/AGVControl/agvControl.csproj b/Cs_HMI/SubProject/AGVControl/agvControl.csproj index 5b08323..2a23f17 100644 --- a/Cs_HMI/SubProject/AGVControl/agvControl.csproj +++ b/Cs_HMI/SubProject/AGVControl/agvControl.csproj @@ -57,9 +57,7 @@ GuideSensor.cs - - Component - + MapControl.cs