diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs index aacf23f..2a29756 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs @@ -68,30 +68,36 @@ namespace AGVNavigationCore.Controls { if (!_showGrid) return; - var bounds = GetVisibleBounds(); var gridSize = (int)(GRID_SIZE * _zoomFactor); - if (gridSize < 5) return; // 너무 작으면 그리지 않음 - // 그리드 시작 위치 (월드 좌표에서 GRID_SIZE의 배수 찾기) - int startX = (bounds.Left / GRID_SIZE) * GRID_SIZE; - int startY = (bounds.Top / GRID_SIZE) * GRID_SIZE; + // 화면 전체를 덮는 월드 좌표 범위 계산 + var topLeft = ScreenToWorld(new Point(0, 0)); + var bottomRight = ScreenToWorld(new Point(Width, Height)); - // 월드 좌표로 그리드 라인 계산 (Transform이 자동으로 적용됨) - for (int x = startX; x <= bounds.Right; x += GRID_SIZE) - { - if (x % (GRID_SIZE * 5) == 0) - g.DrawLine(new Pen(Color.Gray, 1), x, bounds.Top, x, bounds.Bottom); - else - g.DrawLine(_gridPen, x, bounds.Top, x, bounds.Bottom); - } + // 그리드 시작 위치 (월드 좌표에서 GRID_SIZE의 배수로 정렬) + int startX = (topLeft.X / GRID_SIZE) * GRID_SIZE - GRID_SIZE; + int startY = (topLeft.Y / GRID_SIZE) * GRID_SIZE - GRID_SIZE; + int endX = bottomRight.X + GRID_SIZE; + int endY = bottomRight.Y + GRID_SIZE; - for (int y = startY; y <= bounds.Bottom; y += GRID_SIZE) + // 그리드 펜 (가는 선과 굵은 선) + using (var thinPen = new Pen(Color.FromArgb(200, 200, 200), 1)) + using (var thickPen = new Pen(Color.FromArgb(150, 150, 150), 1)) { - if (y % (GRID_SIZE * 5) == 0) - g.DrawLine(new Pen(Color.Gray, 1), bounds.Left, y, bounds.Right, y); - else - g.DrawLine(_gridPen, bounds.Left, y, bounds.Right, y); + // 수직선 그리기 + for (int x = startX; x <= endX; x += GRID_SIZE) + { + var pen = (x % (GRID_SIZE * 5) == 0) ? thickPen : thinPen; + g.DrawLine(pen, x, startY, x, endY); + } + + // 수평선 그리기 + for (int y = startY; y <= endY; y += GRID_SIZE) + { + var pen = (y % (GRID_SIZE * 5) == 0) ? thickPen : thinPen; + g.DrawLine(pen, startX, y, endX, y); + } } } @@ -1563,41 +1569,51 @@ namespace AGVNavigationCore.Controls g.DrawImage(_companyLogo, logoRect); } - // 측정 정보 - if (!string.IsNullOrEmpty(_measurementInfo)) + // 줌 및 스케일 정보 (동적 계산) + // 스케일: 1픽셀 = GRID_SIZE / _zoomFactor mm + // 예: GRID_SIZE=10, zoom=1.0 → 1:10, zoom=0.1 → 1:100 + double scaleRatio = GRID_SIZE / _zoomFactor; + var zoomText = $"Zoom: {_zoomFactor:P0}"; + var scaleText = $"스케일: 1:{scaleRatio:F0}"; + + using (var font = new Font("맑은 고딕", 10, FontStyle.Bold)) + using (var bgBrush = new SolidBrush(Color.FromArgb(220, Color.White))) { - var font = new Font("Arial", 9); - var textBrush = new SolidBrush(Color.Black); - var backgroundBrush = new SolidBrush(Color.FromArgb(200, Color.White)); + // 줌 정보 (좌하단) + var zoomSize = g.MeasureString(zoomText, font); + var zoomRect = new RectangleF(10, Height - zoomSize.Height - 15, zoomSize.Width + 10, zoomSize.Height + 5); + g.FillRectangle(bgBrush, zoomRect); + g.DrawRectangle(Pens.Gray, zoomRect.X, zoomRect.Y, zoomRect.Width, zoomRect.Height); + g.DrawString(zoomText, font, Brushes.Black, zoomRect.X + 5, zoomRect.Y + 2); - var textSize = g.MeasureString(_measurementInfo, font); - var textRect = new Rectangle( - Width - (int)textSize.Width - 20, - Height - (int)textSize.Height - 20, - (int)textSize.Width + 10, - (int)textSize.Height + 10 - ); - - g.FillRectangle(backgroundBrush, textRect); - g.DrawRectangle(Pens.Gray, textRect); - g.DrawString(_measurementInfo, font, textBrush, textRect.X + 5, textRect.Y + 5); - - font.Dispose(); - textBrush.Dispose(); - backgroundBrush.Dispose(); + // 스케일 정보 (줌 정보 위에) + var scaleSize = g.MeasureString(scaleText, font); + var scaleRect = new RectangleF(10, Height - zoomSize.Height - scaleSize.Height - 25, scaleSize.Width + 10, scaleSize.Height + 5); + g.FillRectangle(bgBrush, scaleRect); + g.DrawRectangle(Pens.Gray, scaleRect.X, scaleRect.Y, scaleRect.Width, scaleRect.Height); + g.DrawString(scaleText, font, Brushes.Black, scaleRect.X + 5, scaleRect.Y + 2); } - // 줌 정보 - var zoomText = $"Zoom: {_zoomFactor:P0}"; - var zoomFont = new Font("Arial", 10, FontStyle.Bold); - var zoomSize = g.MeasureString(zoomText, zoomFont); - var zoomPoint = new Point(10, Height - (int)zoomSize.Height - 10); + // 측정 정보 (우하단 - 사용자 정의 정보가 있을 경우) + if (!string.IsNullOrEmpty(_measurementInfo)) + { + using (var font = new Font("맑은 고딕", 9)) + using (var textBrush = new SolidBrush(Color.Black)) + using (var backgroundBrush = new SolidBrush(Color.FromArgb(200, Color.White))) + { + var textSize = g.MeasureString(_measurementInfo, font); + var textRect = new Rectangle( + Width - (int)textSize.Width - 20, + Height - (int)textSize.Height - 20, + (int)textSize.Width + 10, + (int)textSize.Height + 10 + ); - g.FillRectangle(new SolidBrush(Color.FromArgb(200, Color.White)), - zoomPoint.X - 5, zoomPoint.Y - 5, - zoomSize.Width + 10, zoomSize.Height + 10); - g.DrawString(zoomText, zoomFont, Brushes.Black, zoomPoint); - zoomFont.Dispose(); + g.FillRectangle(backgroundBrush, textRect); + g.DrawRectangle(Pens.Gray, textRect); + g.DrawString(_measurementInfo, font, textBrush, textRect.X + 5, textRect.Y + 5); + } + } } /// diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs index 5ec47da..d053046 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs @@ -83,7 +83,7 @@ namespace AGVNavigationCore.Controls // UI 요소들 private Image _companyLogo; private string _companyLogoPath = string.Empty; - private string _measurementInfo = "스케일: 1:100\n면적: 1000㎡\n최종 수정: " + DateTime.Now.ToString("yyyy-MM-dd"); + private string _measurementInfo = string.Empty; // 편집 관련 (EditMode에서만 사용) private bool _isDragging; diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Core/AGVPathResult.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Core/AGVPathResult.cs index fa4b925..819f043 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Core/AGVPathResult.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Core/AGVPathResult.cs @@ -292,6 +292,23 @@ namespace AGVNavigationCore.PathFinding.Core $"계산시간: {CalculationTimeMs}ms"; } + /// + /// 경로의 노드 정보를 포함 + /// + /// + public string GetDetailedPathInfo() + { + if (!Success) + { + return $"경로 계산 실패: {ErrorMessage} (계산시간: {CalculationTimeMs}ms)"; + } + + var data = DetailedPath.Select(t => { + return $"{t.RfidId}[{t.NodeId}] {t.MotorDirection.ToString().Substring(0,1)}-{t.MagnetDirection.ToString().Substring(0,1)}"; + }); + return string.Join(" → ",data); + } + /// /// 단순 경로 목록 반환 (호환성용 - 노드 ID 문자열 목록) diff --git a/Cs_HMI/AGVLogic/AGVSimulator/AGVSimulator.csproj b/Cs_HMI/AGVLogic/AGVSimulator/AGVSimulator.csproj index 5acec01..c79da87 100644 --- a/Cs_HMI/AGVLogic/AGVSimulator/AGVSimulator.csproj +++ b/Cs_HMI/AGVLogic/AGVSimulator/AGVSimulator.csproj @@ -45,6 +45,7 @@ + Form diff --git a/Cs_HMI/AGVLogic/AGVSimulator/Forms/PathTestLogItem.cs b/Cs_HMI/AGVLogic/AGVSimulator/Forms/PathTestLogItem.cs new file mode 100644 index 0000000..d821e09 --- /dev/null +++ b/Cs_HMI/AGVLogic/AGVSimulator/Forms/PathTestLogItem.cs @@ -0,0 +1,25 @@ +using System; + +namespace AGVSimulator.Forms +{ + /// + /// 경로 예측 테스트 결과 로그 항목 + /// + public class PathTestLogItem + { + public string PreviousPosition { get; set; } + public string MotorDirection { get; set; } // 정방향 or 역방향 + public string CurrentPosition { get; set; } + public string TargetPosition { get; set; } + public string DockingPosition { get; set; } // 도킹위치 + public bool Success { get; set; } + public string Message { get; set; } + public string DetailedPath { get; set; } + public DateTime Timestamp { get; set; } + + public PathTestLogItem() + { + Timestamp = DateTime.Now; + } + } +} diff --git a/Cs_HMI/AGVLogic/AGVSimulator/Forms/ProgressLogForm.cs b/Cs_HMI/AGVLogic/AGVSimulator/Forms/ProgressLogForm.cs index 1493b57..eee4823 100644 --- a/Cs_HMI/AGVLogic/AGVSimulator/Forms/ProgressLogForm.cs +++ b/Cs_HMI/AGVLogic/AGVSimulator/Forms/ProgressLogForm.cs @@ -7,26 +7,6 @@ using System.Windows.Forms; namespace AGVSimulator.Forms { - /// - /// 경로 예측 테스트 결과 로그 항목 - /// - public class PathTestLogItem - { - public string PreviousPosition { get; set; } - public string MotorDirection { get; set; } // 정방향 or 역방향 - public string CurrentPosition { get; set; } - public string TargetPosition { get; set; } - public string DockingPosition { get; set; } // 도킹위치 - public bool Success { get; set; } - public string Message { get; set; } - public string DetailedPath { get; set; } - public DateTime Timestamp { get; set; } - - public PathTestLogItem() - { - Timestamp = DateTime.Now; - } - } /// /// 경로 예측 테스트 진행 상황 로그 표시 폼 diff --git a/Cs_HMI/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs b/Cs_HMI/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs index 843d2b4..5d0bd64 100644 --- a/Cs_HMI/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs +++ b/Cs_HMI/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs @@ -325,7 +325,7 @@ namespace AGVSimulator.Forms // 도킹 검증이 없는 경우 추가 검증 수행 if (advancedResult.DockingValidation == null || !advancedResult.DockingValidation.IsValidationRequired) { - if(advancedResult.Path.Count < 1) + if (advancedResult.Path.Count < 1) { } @@ -1228,7 +1228,7 @@ namespace AGVSimulator.Forms _statusLabel.Text = "초기화 완료"; } - + private async void toolStripButton1_Click(object sender, EventArgs e) { // 맵과 AGV 확인 @@ -1382,13 +1382,15 @@ namespace AGVSimulator.Forms { logItem.Success = true; logItem.Message = "성공"; - logItem.DetailedPath = string.Join(" → ", currentPath.GetDetailedInfo()); + //logItem.DetailedPath = string.Join(" → ", currentPath.GetDetailedInfo()); + logItem.DetailedPath = currentPath.GetDetailedPathInfo(); } else { logItem.Success = false; logItem.Message = $"도킹 검증 실패: {dockingValidation.ValidationError}"; - logItem.DetailedPath = string.Join(" → ", currentPath.GetDetailedInfo()); + //logItem.DetailedPath = string.Join(" → ", currentPath.GetDetailedInfo()); + logItem.DetailedPath = currentPath.GetDetailedPathInfo(); } } else