..
This commit is contained in:
@@ -24,6 +24,7 @@ namespace AGVControl
|
||||
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)
|
||||
{
|
||||
@@ -81,7 +82,7 @@ namespace AGVControl
|
||||
private const int SELECTION_DISTANCE = 15; // 선택 가능 거리를 늘림
|
||||
private bool isDraggingText = false;
|
||||
private Point dragOffset;
|
||||
private List<Point> currentPath;
|
||||
// private List<Point> currentPath;
|
||||
|
||||
|
||||
public MapText SelectedText => selectedText;
|
||||
@@ -210,6 +211,7 @@ namespace AGVControl
|
||||
|
||||
public RFIDPoint FindRFIDPoint(uint rfidValue)
|
||||
{
|
||||
if (rfidPoints == null || rfidPoints.Any() == false) return null;
|
||||
return rfidPoints.FirstOrDefault(r => r.RFIDValue == rfidValue);
|
||||
}
|
||||
|
||||
@@ -220,6 +222,57 @@ namespace AGVControl
|
||||
{
|
||||
agv.CurrentPosition = rfidPoint.Location;
|
||||
agv.CurrentRFID = rfidValue;
|
||||
|
||||
// 목적지가 설정되어 있고 경로가 있는 경우 검증
|
||||
if (agv.TargetPosition != Point.Empty && agv.CurrentPath.Count > 0)
|
||||
{
|
||||
// 현재 위치가 경로에 있는지 확인
|
||||
bool isOnPath = agv.CurrentPath.Contains(agv.CurrentPosition);
|
||||
|
||||
if (!isOnPath)
|
||||
{
|
||||
// 경로를 벗어났으므로 새로운 경로 계산
|
||||
var pathResult = CalculatePath(agv.CurrentRFID, agv.TargetRFID);
|
||||
if (pathResult.Success)
|
||||
{
|
||||
SetCurrentPath(pathResult.Path);
|
||||
}
|
||||
}
|
||||
|
||||
// AGV의 방향 결정
|
||||
if (agv.CurrentPath.Count > 0)
|
||||
{
|
||||
// 다음 목적지 찾기
|
||||
var nextPoint = agv.CurrentPath[0];
|
||||
|
||||
// 회전 가능 여부를 고려하여 방향 결정
|
||||
agv.TargetDirection = DetermineDirection(agv.CurrentPosition, nextPoint, agv.TargetPosition);
|
||||
}
|
||||
}
|
||||
|
||||
// 목적지 RFID에 도착했고, 해당 RFID에 고정방향이 있으면 TargetDirection을 강제 설정
|
||||
if (agv.TargetRFID == rfidValue)
|
||||
{
|
||||
var destRFID = FindRFIDPoint(rfidValue);
|
||||
if (destRFID != null && destRFID.FixedDirection.HasValue)
|
||||
{
|
||||
agv.TargetDirection = destRFID.FixedDirection.Value;
|
||||
}
|
||||
}
|
||||
|
||||
this.Invalidate();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool SetTargetPosition(uint rfidValue)
|
||||
{
|
||||
var rfidPoint = FindRFIDPoint(rfidValue);
|
||||
if (rfidPoint != null)
|
||||
{
|
||||
agv.TargetPosition = rfidPoint.Location;
|
||||
agv.TargetRFID = rfidValue;
|
||||
this.Invalidate();
|
||||
return true;
|
||||
}
|
||||
@@ -566,7 +619,7 @@ namespace AGVControl
|
||||
|
||||
if (path.Count > 0)
|
||||
{
|
||||
DrawPath(path);
|
||||
SetCurrentPath(path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,22 +658,22 @@ namespace AGVControl
|
||||
return retval;
|
||||
}
|
||||
|
||||
var path = CalculatePath(startPoint.Location, endPoint.Location);
|
||||
if (path != null && path.Count > 0)
|
||||
retval.Path = CalculatePath(startPoint.Location, endPoint.Location);
|
||||
if (retval.Path != null && retval.Path.Any())
|
||||
{
|
||||
DrawPath(path);
|
||||
//SetCurrentPath(retval.Path);
|
||||
|
||||
// 경로 상의 모든 RFID 값을 가져옴
|
||||
var rfidPath = new List<uint>();
|
||||
foreach (var point in path)
|
||||
{
|
||||
var rfid = GetRFIDPoints()
|
||||
.FirstOrDefault(r => r.Location == point);
|
||||
if (rfid != null)
|
||||
{
|
||||
rfidPath.Add(rfid.RFIDValue);
|
||||
}
|
||||
}
|
||||
//// 경로 상의 모든 RFID 값을 가져옴
|
||||
//var rfidPath = new List<uint>();
|
||||
//foreach (var point in path)
|
||||
//{
|
||||
// var rfid = GetRFIDPoints()
|
||||
// .FirstOrDefault(r => r.Location == point);
|
||||
// if (rfid != null)
|
||||
// {
|
||||
// rfidPath.Add(rfid.RFIDValue);
|
||||
// }
|
||||
//}
|
||||
retval.Success = true;
|
||||
}
|
||||
else
|
||||
@@ -683,8 +736,13 @@ namespace AGVControl
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCurrentPath(rlt.Path); //현재 경로로 설정함
|
||||
MessageBox.Show($"경로가 계산되었습니다.\nRFID 순서: {string.Join(" -> ", rlt.Path)}",
|
||||
"경로 계산", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
if (SetTargetPosition(vend) == false)
|
||||
{
|
||||
MessageBox.Show("목적지 설정 실패");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "pos":
|
||||
@@ -700,12 +758,12 @@ namespace AGVControl
|
||||
od.Filter = "path data|*.route";
|
||||
od.FilterIndex = 0;
|
||||
od.RestoreDirectory = true;
|
||||
if(filename.isEmpty()==false)
|
||||
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;
|
||||
@@ -734,7 +792,7 @@ namespace AGVControl
|
||||
|
||||
if (od.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
|
||||
|
||||
this.LoadFromFile(filename, out string errmsg);
|
||||
if (errmsg.isEmpty() == false) UTIL.MsgE(errmsg);
|
||||
this.Invalidate();
|
||||
@@ -935,20 +993,21 @@ namespace AGVControl
|
||||
|
||||
if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
// 화면 좌표를 맵 좌표로 변환
|
||||
var mapPoint = ScreenToMap(e.Location);
|
||||
|
||||
//RFID 포인트 찾기
|
||||
var selected_rfid = rfidPoints.Where(t => t.Bounds.Expand(SELECTION_DISTANCE, SELECTION_DISTANCE).Contains(mapPoint)).FirstOrDefault();
|
||||
if (selected_rfid != null)
|
||||
{
|
||||
var rlt = AR.UTIL.InputBox("rfid value", selected_rfid.RFIDValue.ToString());
|
||||
if (rlt.Item1 == true)
|
||||
{
|
||||
selected_rfid.RFIDValue = uint.Parse(rlt.Item2);// dialog.InputText;
|
||||
ProcessRFIDConnections();
|
||||
this.Invalidate();
|
||||
}
|
||||
// 고정방향 순환 변경: 없음→전진→후진→없음
|
||||
if (!selected_rfid.FixedDirection.HasValue)
|
||||
selected_rfid.FixedDirection = Direction.Forward;
|
||||
else if (selected_rfid.FixedDirection == Direction.Forward)
|
||||
selected_rfid.FixedDirection = Direction.Backward;
|
||||
else
|
||||
selected_rfid.FixedDirection = null;
|
||||
|
||||
this.Invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -959,7 +1018,7 @@ namespace AGVControl
|
||||
var rlt = AR.UTIL.InputBox("input", selected_txt.Text);
|
||||
if (rlt.Item1 == true)
|
||||
{
|
||||
selected_txt.Text = rlt.Item2;// dialog.InputText;
|
||||
selected_txt.Text = rlt.Item2;
|
||||
this.Invalidate();
|
||||
}
|
||||
return;
|
||||
@@ -1108,9 +1167,9 @@ namespace AGVControl
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawPath(List<Point> path)
|
||||
public void SetCurrentPath(List<Point> path)
|
||||
{
|
||||
currentPath = path;
|
||||
agv.CurrentPath = path;
|
||||
this.Invalidate();
|
||||
}
|
||||
|
||||
@@ -1130,6 +1189,7 @@ namespace AGVControl
|
||||
|
||||
DrawPath(e.Graphics);
|
||||
DrawAGV(e.Graphics);
|
||||
DrawTargetFlag(e.Graphics); // 목적지 깃발 그리기 추가
|
||||
|
||||
// 선택된 개체 강조 표시
|
||||
if (selectedRFID != null)
|
||||
@@ -1180,51 +1240,85 @@ namespace AGVControl
|
||||
var MarkerSize = 5;
|
||||
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);
|
||||
|
||||
// 회전 가능 여부에 따라 다른 색상 사용
|
||||
using (var brush = new SolidBrush(rfid.IsRotatable ? Color.Yellow : Color.Green))
|
||||
{
|
||||
g.FillEllipse(brush, rfid.Bounds);
|
||||
}
|
||||
// 고정방향이 있으면 테두리 색상 표시
|
||||
if (rfid.FixedDirection.HasValue)
|
||||
{
|
||||
Color borderColor = rfid.FixedDirection.Value == Direction.Forward ? Color.DeepSkyBlue : Color.Gold; using (var pen = new Pen(borderColor, 2))
|
||||
{
|
||||
g.DrawEllipse(pen, rfid.Bounds.Expand(5,5));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RFID 포인트 그리기
|
||||
// RFID 값 표시
|
||||
foreach (var rfid in rfidPoints)
|
||||
{
|
||||
//g.FillEllipse(Brushes.Green, rfid.Location.X - 3, rfid.Location.Y - 3, 6, 6);
|
||||
var tagstr = $"{rfid.RFIDValue}";
|
||||
var fsize = g.MeasureString(tagstr, Font);
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void DrawAGV(Graphics g)
|
||||
{
|
||||
var agvsize = 30;
|
||||
var halfsize = (int)(agvsize / 2);
|
||||
var rect = new Rectangle(agv.CurrentPosition.X - halfsize,
|
||||
agv.CurrentPosition.Y - halfsize,
|
||||
agvsize, agvsize);
|
||||
|
||||
var recti = new Rectangle(rect.X + 5, rect.Y + 5, rect.Width - 10, rect.Height - 10);
|
||||
// AGV의 현재 위치를 중심으로 하는 원
|
||||
var circleRect = new Rectangle(
|
||||
agv.CurrentPosition.X - halfsize,
|
||||
agv.CurrentPosition.Y - halfsize,
|
||||
agvsize, agvsize);
|
||||
|
||||
using (var sb = new SolidBrush(Color.FromArgb(150, Color.Gold)))
|
||||
g.FillEllipse(sb, rect);
|
||||
// 삼각형 화살표를 위한 포인트 배열
|
||||
Point[] trianglePoints = new Point[3];
|
||||
var arrowSize = halfsize - 5; // 삼각형 크기
|
||||
|
||||
using (var pen = new Pen(Color.Black))
|
||||
g.DrawEllipse(pen, rect);
|
||||
// AGV의 방향에 따라 삼각형 포인트 계산
|
||||
switch (agv.CurrentDirection)
|
||||
{
|
||||
case Direction.Forward:
|
||||
trianglePoints[0] = new Point(agv.CurrentPosition.X, agv.CurrentPosition.Y - arrowSize);
|
||||
trianglePoints[1] = new Point(agv.CurrentPosition.X - arrowSize, agv.CurrentPosition.Y + arrowSize);
|
||||
trianglePoints[2] = new Point(agv.CurrentPosition.X + arrowSize, agv.CurrentPosition.Y + arrowSize);
|
||||
break;
|
||||
case Direction.Backward:
|
||||
trianglePoints[0] = new Point(agv.CurrentPosition.X, agv.CurrentPosition.Y + arrowSize);
|
||||
trianglePoints[1] = new Point(agv.CurrentPosition.X - arrowSize, agv.CurrentPosition.Y - arrowSize);
|
||||
trianglePoints[2] = new Point(agv.CurrentPosition.X + arrowSize, agv.CurrentPosition.Y - arrowSize);
|
||||
break;
|
||||
}
|
||||
|
||||
using (var sb = new SolidBrush(Color.FromArgb(150, Color.White)))
|
||||
g.FillEllipse(sb, recti);
|
||||
|
||||
using (var pen = new Pen(Color.Black))
|
||||
g.DrawEllipse(pen, recti);
|
||||
|
||||
// 원 그리기
|
||||
Color bgcolor = agv.BatteryLevel > 80 ? Color.Lime : ( agv.BatteryLevel > 60 ? Color.Gold : Color.Tomato );
|
||||
using (var circleBrush = new SolidBrush(Color.FromArgb(150, bgcolor)))
|
||||
g.FillEllipse(circleBrush, circleRect);
|
||||
using (var circlePen = new Pen(Color.Black, 2))
|
||||
g.DrawEllipse(circlePen, circleRect);
|
||||
|
||||
// 삼각형 화살표 그리기
|
||||
using (var arrowBrush = new SolidBrush(Color.FromArgb(200, Color.White)))
|
||||
g.FillPolygon(arrowBrush, trianglePoints);
|
||||
using (var arrowPen = new Pen(Color.Black, 2))
|
||||
g.DrawPolygon(arrowPen, trianglePoints);
|
||||
|
||||
//g.DrawImage(Properties.Resources.ico_navi_40, circleRect);
|
||||
}
|
||||
|
||||
private void DrawCustomLines(Graphics g)
|
||||
@@ -1358,7 +1452,7 @@ namespace AGVControl
|
||||
|
||||
private void DrawPath(Graphics g)
|
||||
{
|
||||
if (currentPath == null || currentPath.Count < 2)
|
||||
if (agv.CurrentPath == null || agv.CurrentPath.Count < 2)
|
||||
return;
|
||||
|
||||
Color pathColor = Color.FromArgb(100, Color.Lime);
|
||||
@@ -1367,9 +1461,9 @@ namespace AGVControl
|
||||
using (Pen pathPen = new Pen(pathColor, pathWidth))
|
||||
{
|
||||
pathPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
|
||||
for (int i = 0; i < currentPath.Count - 1; i++)
|
||||
for (int i = 0; i < agv.CurrentPath.Count - 1; i++)
|
||||
{
|
||||
g.DrawLine(pathPen, currentPath[i], currentPath[i + 1]);
|
||||
g.DrawLine(pathPen, agv.CurrentPath[i], agv.CurrentPath[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1728,5 +1822,85 @@ namespace AGVControl
|
||||
g.DrawString(text, font, Brushes.Black, rect, format);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTargetFlag(Graphics g)
|
||||
{
|
||||
if (agv.TargetPosition == Point.Empty) return;
|
||||
|
||||
// 바닥에 흰색 원 그리기
|
||||
using (var baseBrush = new SolidBrush(Color.Red))
|
||||
using (var basePen = new Pen(Color.Black, 1))
|
||||
{
|
||||
var baseSize = 8;
|
||||
g.FillEllipse(baseBrush,
|
||||
agv.TargetPosition.X - baseSize / 2,
|
||||
agv.TargetPosition.Y - baseSize / 2,
|
||||
baseSize, baseSize);
|
||||
g.DrawEllipse(basePen,
|
||||
agv.TargetPosition.X - baseSize / 2,
|
||||
agv.TargetPosition.Y - baseSize / 2,
|
||||
baseSize, baseSize);
|
||||
}
|
||||
|
||||
// 깃대 그리기 (길이를 2/3로 줄임)
|
||||
using (var polePen = new Pen(Color.Brown, 3))
|
||||
{
|
||||
var poleLength = 27; // 40 * 2/3 ≈ 27
|
||||
g.DrawLine(polePen,
|
||||
agv.TargetPosition.X,
|
||||
agv.TargetPosition.Y,
|
||||
agv.TargetPosition.X,
|
||||
agv.TargetPosition.Y - poleLength);
|
||||
}
|
||||
|
||||
// 깃발 그리기
|
||||
Point[] flagPoints = new Point[3];
|
||||
flagPoints[0] = new Point(agv.TargetPosition.X, agv.TargetPosition.Y - 27); // 깃대 길이에 맞춤
|
||||
flagPoints[1] = new Point(agv.TargetPosition.X + 20, agv.TargetPosition.Y - 22);
|
||||
flagPoints[2] = new Point(agv.TargetPosition.X, agv.TargetPosition.Y - 17);
|
||||
|
||||
using (var flagBrush = new SolidBrush(Color.Red))
|
||||
using (var flagPen = new Pen(Color.DarkRed, 1))
|
||||
{
|
||||
g.FillPolygon(flagBrush, flagPoints);
|
||||
g.DrawPolygon(flagPen, flagPoints);
|
||||
}
|
||||
}
|
||||
|
||||
// 회전 가능 여부 토글 함수
|
||||
public void ToggleRotatable(uint rfidValue)
|
||||
{
|
||||
var rfidPoint = FindRFIDPoint(rfidValue);
|
||||
if (rfidPoint != null)
|
||||
{
|
||||
rfidPoint.IsRotatable = !rfidPoint.IsRotatable;
|
||||
this.Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
// 회전 가능 여부 확인 함수
|
||||
public bool IsPointRotatable(uint rfidValue)
|
||||
{
|
||||
var rfidPoint = FindRFIDPoint(rfidValue);
|
||||
return rfidPoint?.IsRotatable ?? false;
|
||||
}
|
||||
|
||||
// 경로 계산 시 회전 가능 여부를 고려하여 방향 결정
|
||||
private Direction DetermineDirection(Point current, Point next, Point target)
|
||||
{
|
||||
// 현재 위치가 회전 가능한 구간인 경우
|
||||
var currentRFID = rfidPoints.FirstOrDefault(p => p.Location == current);
|
||||
if (currentRFID?.IsRotatable ?? false)
|
||||
{
|
||||
// 목적지 방향으로 직접 방향 결정
|
||||
if (target.X > current.X) return Direction.Forward;
|
||||
if (target.X < current.X) return Direction.Backward;
|
||||
if (target.Y > current.Y) return Direction.Forward;
|
||||
if (target.Y < current.Y) return Direction.Backward;
|
||||
}
|
||||
|
||||
// 회전 불가능한 구간인 경우 현재 진행 방향 유지
|
||||
return agv.CurrentDirection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user