This commit is contained in:
chi
2025-05-27 16:32:53 +09:00
parent 9f4bec4142
commit ddf0b1907d
9 changed files with 453 additions and 315 deletions

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
@@ -11,6 +12,8 @@ using System.Security.Permissions;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using AGVControl.Models;
using AR;
using static System.Net.Mime.MediaTypeNames;
namespace AGVControl
{
@@ -27,7 +30,7 @@ namespace AGVControl
if (obj is RFIDConnection other)
{
return (StartRFID == other.StartRFID && EndRFID == other.EndRFID) ||
(IsBidirectional && other.IsBidirectional &&
(IsBidirectional && other.IsBidirectional &&
StartRFID == other.EndRFID && EndRFID == other.StartRFID);
}
return false;
@@ -108,18 +111,11 @@ namespace AGVControl
// 툴바 버튼 영역 초기화
UpdateToolbarRects();
// 마우스 이벤트 핸들러 추가
this.MouseWheel += MapControl_MouseWheel;
this.MouseDown += MapControl_MouseDown;
this.MouseMove += MapControl_MouseMove;
this.MouseUp += MapControl_MouseUp;
this.MouseClick += MapControl_MouseClick;
this.MouseDoubleClick += MapControl_MouseDoubleClick;
this.Resize += MapControl_Resize;
}
private void MapControl_Resize(object sender, EventArgs e)
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
UpdateToolbarRects();
this.Invalidate();
}
@@ -229,8 +225,10 @@ namespace AGVControl
}
return false;
}
private void MapControl_MouseWheel(object sender, MouseEventArgs e)
protected override void OnMouseWheel(MouseEventArgs e)
{
base.OnMouseWheel(e);
if (e.Delta > 0)
{
zoom *= 1.1f;
@@ -244,8 +242,10 @@ namespace AGVControl
this.Invalidate();
}
private void MapControl_MouseDown(object sender, MouseEventArgs e)
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
lastMousePosition = e.Location;
var mapPoint = ScreenToMap(e.Location);
@@ -323,8 +323,11 @@ namespace AGVControl
Point? lineStartPoint = null;
Point? branchPoint = null;
private void MapControl_MouseMove(object sender, MouseEventArgs e)
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
currentMousePosition = e.Location;
var mapPoint = ScreenToMap(e.Location);
@@ -416,12 +419,10 @@ namespace AGVControl
if (line.StartPoint == draggingPoint.Value)
{
line.StartPoint = selectedRFID.Location;
line.Distance = GetDistance(line.StartPoint, line.EndPoint);
}
if (line.EndPoint == draggingPoint.Value)
{
line.EndPoint = selectedRFID.Location;
line.Distance = GetDistance(line.StartPoint, line.EndPoint);
}
}
}
@@ -495,8 +496,9 @@ namespace AGVControl
}
private void MapControl_MouseUp(object sender, MouseEventArgs e)
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (e.Button == MouseButtons.Middle)
{
isDragging = false;
@@ -515,7 +517,7 @@ namespace AGVControl
var rfidLines = GetRFIDLines();
var directConnection = rfidLines.FirstOrDefault(line =>
(line.StartPoint == a && line.EndPoint == b) ||
(line.IsBidirectional && line.StartPoint == b && line.EndPoint == a));
(line.StartPoint == b && line.EndPoint == a));
if (directConnection != null)
{
@@ -541,10 +543,7 @@ namespace AGVControl
else if (line.EndPoint == point)
{
// 양방향 연결인 경우에만 시작점을 이웃으로 추가
if (line.IsBidirectional)
{
neighbors.Add(line.StartPoint);
}
neighbors.Add(line.StartPoint);
}
}
@@ -631,8 +630,9 @@ namespace AGVControl
return retval;
}
private void MapControl_MouseClick(object sender, MouseEventArgs e)
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
var mapPoint = ScreenToMap(e.Location);
if (e.Button == MouseButtons.Right)
{
@@ -700,8 +700,12 @@ namespace AGVControl
od.Filter = "path data|*.route";
od.FilterIndex = 0;
od.RestoreDirectory = true;
od.FileName = System.IO.Path.GetFileName(this.filename);
od.InitialDirectory = System.IO.Path.GetDirectoryName(this.filename);
if(filename.isEmpty()==false)
{
od.FileName = System.IO.Path.GetFileName(filename);
od.InitialDirectory = System.IO.Path.GetDirectoryName(filename);
}
if (od.ShowDialog() == DialogResult.OK)
{
filename = od.FileName;
@@ -730,8 +734,9 @@ namespace AGVControl
if (od.ShowDialog() == DialogResult.OK)
{
filename = od.FileName;
this.LoadFromFile(filename);
this.LoadFromFile(filename, out string errmsg);
if (errmsg.isEmpty() == false) UTIL.MsgE(errmsg);
this.Invalidate();
}
}
@@ -825,9 +830,6 @@ namespace AGVControl
{
StartPoint = previewStartPoint.Value,
EndPoint = clickedRFID.Location,
StartRFID = startRFID.RFIDValue,
EndRFID = clickedRFID.RFIDValue,
Distance = GetDistance(previewStartPoint.Value, clickedRFID.Location)
};
rfidLines.Add(line);
selectedRFIDLine = line;
@@ -872,7 +874,7 @@ namespace AGVControl
nearbyPoints.Sort((a, b) => a.ProjectionRatio.CompareTo(b.ProjectionRatio));
// 이전 RFID 값과 위치를 저장
uint prevRFID = line.StartRFID;
//uint prevRFID = line.StartRFID;
Point prevPoint = line.StartPoint;
// 정렬된 포인트들을 순차적으로 연결
@@ -882,15 +884,12 @@ namespace AGVControl
{
StartPoint = prevPoint,
EndPoint = item.Point.Location,
StartRFID = prevRFID,
EndRFID = item.Point.RFIDValue,
Distance = GetDistance(prevPoint, item.Point.Location)
};
rfidLines.Add(rfidLine);
// 현재 포인트를 다음 선분의 시작점으로 설정
prevPoint = item.Point.Location;
prevRFID = item.Point.RFIDValue;
}
// 마지막 포인트와 원래 선의 끝점을 연결
@@ -898,9 +897,6 @@ namespace AGVControl
{
StartPoint = prevPoint,
EndPoint = line.EndPoint,
StartRFID = prevRFID,
EndRFID = line.EndRFID,
Distance = GetDistance(prevPoint, line.EndPoint)
};
rfidLines.Add(finalLine);
@@ -933,38 +929,45 @@ namespace AGVControl
return GetDistance(point, new Point((int)projectionX, (int)projectionY));
}
private void MapControl_MouseDoubleClick(object sender, MouseEventArgs e)
protected override void OnMouseDoubleClick(MouseEventArgs e)
{
base.OnMouseDoubleClick(e);
if (e.Button == MouseButtons.Left)
{
// 화면 좌표를 맵 좌표로 변환
var mapPoint = ScreenToMap(e.Location);
// 텍스트 객체 찾기
foreach (var text in mapTexts)
//RFID 포인트 찾기
var selected_rfid = rfidPoints.Where(t => t.Bounds.Expand(SELECTION_DISTANCE, SELECTION_DISTANCE).Contains(mapPoint)).FirstOrDefault();
if (selected_rfid != null)
{
var textSize = CreateGraphics().MeasureString(text.Text, text.Font);
var rect = new RectangleF(
text.Location.X - SELECTION_DISTANCE,
text.Location.Y - SELECTION_DISTANCE,
textSize.Width + SELECTION_DISTANCE * 2,
textSize.Height + SELECTION_DISTANCE * 2
);
if (rect.Contains(mapPoint))
var rlt = AR.UTIL.InputBox("rfid value", selected_rfid.RFIDValue.ToString());
if (rlt.Item1 == true)
{
var rlt = AR.UTIL.InputBox("input", text.Text);
if (rlt.Item1 == true)
{
text.Text = rlt.Item2;// dialog.InputText;
this.Invalidate();
}
break;
selected_rfid.RFIDValue = uint.Parse(rlt.Item2);// dialog.InputText;
ProcessRFIDConnections();
this.Invalidate();
}
return;
}
// 텍스트 객체 찾기
var selected_txt = mapTexts.Where(t => t.Bounds.Expand(SELECTION_DISTANCE, SELECTION_DISTANCE).Contains(mapPoint)).FirstOrDefault();
if (selected_txt != null)
{
var rlt = AR.UTIL.InputBox("input", selected_txt.Text);
if (rlt.Item1 == true)
{
selected_txt.Text = rlt.Item2;// dialog.InputText;
this.Invalidate();
}
return;
}
}
}
public event EventHandler<MouseEventArgs> OnRightClick;
public void SetRFIDPoints(List<RFIDPoint> points)
@@ -1121,7 +1124,7 @@ namespace AGVControl
e.Graphics.ScaleTransform(zoom, zoom);
DrawRFIDLines(e.Graphics);
DrawMap(e.Graphics);
DrawRFIDPoints(e.Graphics);
//DrawCustomLines(e.Graphics);
DrawMapTexts(e.Graphics);
@@ -1169,13 +1172,15 @@ namespace AGVControl
}
private void DrawMap(Graphics g)
private void DrawRFIDPoints(Graphics g)
{
// RFID 포인트 그리기
foreach (var rfid in rfidPoints)
{
var MarkerSize = 5;
g.FillEllipse(Brushes.Green, rfid.Location.X - (MarkerSize / 2f), rfid.Location.Y - (MarkerSize / 2f), MarkerSize, MarkerSize);
var half = MarkerSize / 2f;
rfid.Bounds = new RectangleF(rfid.Location.X - half, rfid.Location.Y - half, MarkerSize, MarkerSize);
g.FillEllipse(Brushes.Green, rfid.Bounds);
//g.DrawString(rfid.RFIDValue, Font, Brushes.WhiteSmoke, rfid.Location.X + 5, rfid.Location.Y - 5);
}
@@ -1185,9 +1190,16 @@ namespace AGVControl
//g.FillEllipse(Brushes.Green, rfid.Location.X - 3, rfid.Location.Y - 3, 6, 6);
var tagstr = $"{rfid.RFIDValue}";
var fsize = g.MeasureString(tagstr, Font);
g.DrawString(tagstr, Font, Brushes.WhiteSmoke,
(rfid.Location.X - fsize.Width / 2f),
(rfid.Location.Y + 2));
var rect = new RectangleF(rfid.Bounds.X - (fsize.Width / 2f) + 3,
rfid.Bounds.Y + 6,
fsize.Width,
fsize.Height);
//g.DrawRectangle(Pens.Red, rect);
g.DrawString(tagstr, Font, Brushes.WhiteSmoke, rect, new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center,
});
}
}
@@ -1233,18 +1245,22 @@ namespace AGVControl
foreach (var text in mapTexts)
{
var textSize = g.MeasureString(text.Text, text.Font);
var rect = new RectangleF(
text.Location.X,
text.Location.Y,
textSize.Width,
textSize.Height
);
if (text.Dirty)
{
text.Bounds = new RectangleF(
text.Location.X,
text.Location.Y,
textSize.Width,
textSize.Height
);
text.Dirty = false;
}
if (text.BackgroundColor != Color.Transparent)
{
using (var brush = new SolidBrush(text.BackgroundColor))
{
g.FillRectangle(brush, rect);
g.FillRectangle(brush, text.Bounds);
}
}
@@ -1258,7 +1274,7 @@ namespace AGVControl
using (Pen pen = new Pen(Color.Blue, 1))
{
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
g.DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height);
g.DrawRectangle(pen, text.Bounds.X, text.Bounds.Y, text.Bounds.Width, text.Bounds.Height);
}
}
}
@@ -1266,6 +1282,29 @@ namespace AGVControl
private void DrawRFIDLines(Graphics g)
{
var idx = 0;
using (Font f = new Font("arial", 4))
{
foreach (var item in rfidLines)
{
var sp = item.StartPoint;
var ep = item.EndPoint;
using (var p = new Pen(Color.FromArgb(50, Color.White), 10))
{
g.DrawLine(p, sp, ep);
var x = sp.X;
var y = sp.Y;
g.DrawString($"{idx}", f, Brushes.Gold, x, y);
x = ep.X;
y = ep.Y;
g.DrawString($"{idx}", f, Brushes.Pink, x, y);
idx++;
}
}
}
foreach (var connection in rfidConnections)
{
var startPoint = rfidPoints.First(p => p.RFIDValue == connection.StartRFID).Location;
@@ -1351,7 +1390,7 @@ namespace AGVControl
this.Invalidate();
}
public void AddRFIDLine(Point startPoint, Point endPoint, uint startRFID, uint endRFID, bool isBidirectional = false)
public void AddRFIDLine(Point startPoint, Point endPoint)
{
// 시작점과 끝점 사이의 모든 RFID 포인트 찾기
var allPoints = new List<(RFIDPoint Point, float Distance)>();
@@ -1387,47 +1426,14 @@ namespace AGVControl
{
StartPoint = startPoint,
EndPoint = endPoint,
StartRFID = startRFID,
EndRFID = endRFID,
IsBidirectional = isBidirectional,
Distance = GetDistance(startPoint, endPoint)
};
// 연결된 RFID 목록 생성
line.ConnectedRFIDs.Add(startRFID);
foreach (var point in allPoints)
{
line.ConnectedRFIDs.Add(point.Point.RFIDValue);
}
line.ConnectedRFIDs.Add(endRFID);
// 다음/이전 RFID 정보 설정
for (int i = 0; i < line.ConnectedRFIDs.Count - 1; i++)
{
var current = line.ConnectedRFIDs[i];
var next = line.ConnectedRFIDs[i + 1];
line.NextRFID[current] = next;
line.PrevRFID[next] = current;
if (isBidirectional)
{
line.NextRFID[next] = current;
line.PrevRFID[current] = next;
}
}
rfidLines.Add(line);
this.Invalidate();
}
public void LoadMapData(MapData mapData)
{
rfidPoints = mapData.RFIDPoints ?? new List<RFIDPoint>();
mapTexts = mapData.MapTexts ?? new List<MapText>();
customLines = mapData.CustomLines ?? new List<CustomLine>();
rfidLines = mapData.RFIDLines ?? new List<RFIDLine>();
this.Invalidate();
}
public void ClearMap()
{
@@ -1499,10 +1505,13 @@ namespace AGVControl
}
File.WriteAllLines(filename, lines);
this.filename = filename;
}
public string LoadFromFile(string filename)
public bool LoadFromFile(string filename, out string message)
{
this.filename = filename;
message = string.Empty;
ClearMap();
var lines = File.ReadAllLines(filename);
@@ -1528,9 +1537,19 @@ namespace AGVControl
var validY = int.TryParse(rfidParts[1], out int valY);
var validN = uint.TryParse(rfidParts[2], out uint valRfid);
if (validX && validY && validN)
{
if (rfidPoints.Where(t => t.RFIDValue == valRfid).Any())
{
//이미존재한다
var newvalue =
sb.AppendLine($"rfid중복{valRfid}");
}
AddRFIDPoint(new Point(valX, valY), valRfid);
}
else sb.AppendLine($"[{section}] {line}");
}
@@ -1542,10 +1561,7 @@ namespace AGVControl
{
AddRFIDLine(
new Point(int.Parse(rfidLineParts[0]), int.Parse(rfidLineParts[1])),
new Point(int.Parse(rfidLineParts[2]), int.Parse(rfidLineParts[3])),
uint.Parse(rfidLineParts[4]),
uint.Parse(rfidLineParts[5]),
(rfidLineParts[6].ToLower() == "true")
new Point(int.Parse(rfidLineParts[2]), int.Parse(rfidLineParts[3]))
);
}
break;
@@ -1588,7 +1604,8 @@ namespace AGVControl
this.Invalidate();
return sb.ToString();
message = sb.ToString();
return true;
}
private void ProcessRFIDConnections()
@@ -1603,7 +1620,7 @@ namespace AGVControl
// 1. 선 위의 모든 RFID 포인트(시작, 끝 포함)를 projectionRatio로 정렬
var pointsOnThisLine = rfidPoints
.Where(p => IsPointOnLine(p.Location, start, end, 15f)) // 오차 허용치 넉넉히
.Where(p => IsPointOnLine(p.Location, start, end, 10f)) // 오차 허용치 넉넉히
.Select(p => new
{
RFID = p.RFIDValue,
@@ -1611,11 +1628,11 @@ namespace AGVControl
})
.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 });
//// 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();
@@ -1635,7 +1652,6 @@ namespace AGVControl
{
StartRFID = from,
EndRFID = to,
IsBidirectional = line.IsBidirectional,
Distance = GetDistance(fromPt, toPt)
});
connectionSet.Add(key);
@@ -1649,8 +1665,8 @@ namespace AGVControl
var distance = GetDistanceToLine(point, lineStart, lineEnd);
if (distance > tolerance) return false;
var projectionRatio = GetProjectionRatio(point, lineStart, lineEnd);
return projectionRatio >= 0 && projectionRatio <= 1;
var projectionRatio = Math.Round(GetProjectionRatio(point, lineStart, lineEnd), 2);
return projectionRatio >= 0 && projectionRatio <= 1.0;
}
private void DeleteNearbyRFIDLine(Point clickPoint)

View File

@@ -1,14 +1,63 @@
using System.Drawing;
using System;
using System.Collections.Generic;
using System.Collections.Generic;
namespace AGVControl.Models
{
public class MapText
{
public Point Location { get; set; }
public string Text { get; set; }
private bool _dirty = true;
private Point _location;
private string _text;
private Font _font;
public bool Dirty
{
get => _dirty;
set => _dirty = value;
}
public Point Location
{
get => _location;
set
{
if (_location != value)
{
_location = value;
_dirty = true;
}
}
}
public string Text
{
get => _text;
set
{
if (_text != value)
{
_text = value;
_dirty = true;
}
}
}
public Color TextColor { get; set; }
public Color BackgroundColor { get; set; }
public Font Font { get; set; }
public Font Font
{
get => _font;
set
{
if (_font != value)
{
_font = value;
_dirty = true;
}
}
}
public RectangleF Bounds { get; set; }
}
}

View File

@@ -7,14 +7,24 @@ namespace AGVControl.Models
public class RFIDLine
{
public Point StartPoint { get; set; }
public Point EndPoint { get; set; }
public uint StartRFID { get; set; }
public uint EndRFID { get; set; }
public bool IsBidirectional { get; set; } = true; // 양방향 이동 가능 여부
public float Distance { get; set; } // 두 RFID 포인트 사이의 거리
public Point EndPoint { get; set; }
public float Distance
{
get {
float dx = StartPoint.X - EndPoint.X;
float dy = StartPoint.Y - EndPoint.Y;
return (float)Math.Sqrt(dx * dx + dy * dy); // double을 float로 명시적 캐스팅
}
}
public List<uint> ConnectedRFIDs { get; set; } = new List<uint>(); // 연결된 모든 RFID 값들
public Dictionary<uint, uint> NextRFID { get; set; } = new Dictionary<uint, uint>(); // 각 RFID의 다음 RFID
public Dictionary<uint, uint> PrevRFID { get; set; } = new Dictionary<uint, uint>(); // 각 RFID의 이전 RFID
//public uint StartRFID { get; set; }
//public uint EndRFID { get; set; }
//public bool IsBidirectional { get; set; } = true; // 양방향 이동 가능 여부
//public float Distance { get; set; } // 두 RFID 포인트 사이의 거리
//public List<uint> ConnectedRFIDs { get; set; } = new List<uint>(); // 연결된 모든 RFID 값들
//public Dictionary<uint, uint> NextRFID { get; set; } = new Dictionary<uint, uint>(); // 각 RFID의 다음 RFID
//public Dictionary<uint, uint> PrevRFID { get; set; } = new Dictionary<uint, uint>(); // 각 RFID의 이전 RFID
}
}

View File

@@ -9,5 +9,6 @@ namespace AGVControl.Models
public uint RFIDValue { get; set; }
public string NextRFID { get; set; } // 다음 RFID 포인트의 값
public bool IsBidirectional { get; set; } // 양방향 연결 여부
public RectangleF Bounds { get; set; }
}
}