using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using System.Windows.Forms; using AGVNavigationCore.Models; using AGVNavigationCore.PathFinding; namespace AGVNavigationCore.Controls { /// /// 통합 AGV 캔버스 컨트롤 /// 맵 편집, AGV 시뮬레이션, 실시간 모니터링을 모두 지원 /// public partial class UnifiedAGVCanvas : UserControl { #region Constants private const int NODE_SIZE = 24; private const int NODE_RADIUS = NODE_SIZE / 2; private const int GRID_SIZE = 20; private const float CONNECTION_WIDTH = 2.0f; private const int SNAP_DISTANCE = 10; private const int AGV_SIZE = 30; private const int CONNECTION_ARROW_SIZE = 8; #endregion #region Enums /// /// 캔버스 모드 /// public enum CanvasMode { ViewOnly, // 읽기 전용 (시뮬레이터, 모니터링) Edit // 편집 가능 (맵 에디터) } /// /// 편집 모드 (CanvasMode.Edit일 때만 적용) /// public enum EditMode { Select, // 선택 모드 Move, // 이동 모드 AddNode, // 노드 추가 모드 Connect, // 연결 모드 Delete, // 삭제 모드 AddLabel, // 라벨 추가 모드 AddImage // 이미지 추가 모드 } #endregion #region Fields // 캔버스 모드 private CanvasMode _canvasMode = CanvasMode.ViewOnly; private EditMode _editMode = EditMode.Select; // 맵 데이터 private List _nodes; private MapNode _selectedNode; private MapNode _hoveredNode; // AGV 관련 private List _agvList; private Dictionary _agvPositions; private Dictionary _agvDirections; private Dictionary _agvStates; // 경로 관련 private PathResult _currentPath; private List _allPaths; // UI 요소들 private Image _companyLogo; private string _companyLogoPath = string.Empty; private string _measurementInfo = "스케일: 1:100\n면적: 1000㎡\n최종 수정: " + DateTime.Now.ToString("yyyy-MM-dd"); // 편집 관련 (EditMode에서만 사용) private bool _isDragging; private Point _dragOffset; private Point _lastMousePosition; private bool _isConnectionMode; private MapNode _connectionStartNode; private Point _connectionEndPoint; // 그리드 및 줌 관련 private bool _showGrid = true; private float _zoomFactor = 1.0f; private Point _panOffset = Point.Empty; private bool _isPanning; // 자동 증가 카운터 private int _nodeCounter = 1; // 브러쉬 및 펜 private Brush _normalNodeBrush; private Brush _rotationNodeBrush; private Brush _dockingNodeBrush; private Brush _chargingNodeBrush; private Brush _selectedNodeBrush; private Brush _hoveredNodeBrush; private Brush _gridBrush; private Brush _agvBrush; private Brush _pathBrush; private Pen _connectionPen; private Pen _gridPen; private Pen _tempConnectionPen; private Pen _selectedNodePen; private Pen _pathPen; private Pen _agvPen; // 컨텍스트 메뉴 private ContextMenuStrip _contextMenu; #endregion #region Events // 맵 편집 이벤트 public event EventHandler NodeAdded; public event EventHandler NodeSelected; public event EventHandler NodeDeleted; public event EventHandler NodeMoved; public event EventHandler MapChanged; // AGV 이벤트 public event EventHandler AGVSelected; public event EventHandler AGVStateChanged; #endregion #region Properties /// /// 캔버스 모드 /// public CanvasMode Mode { get => _canvasMode; set { _canvasMode = value; UpdateModeUI(); Invalidate(); } } /// /// 편집 모드 (CanvasMode.Edit일 때만 적용) /// public EditMode CurrentEditMode { get => _editMode; set { if (_canvasMode != CanvasMode.Edit) return; _editMode = value; if (_editMode != EditMode.Connect) { CancelConnection(); } Cursor = GetCursorForMode(_editMode); Invalidate(); } } /// /// 그리드 표시 여부 /// public bool ShowGrid { get => _showGrid; set { _showGrid = value; Invalidate(); } } /// /// 줌 팩터 /// public float ZoomFactor { get => _zoomFactor; set { _zoomFactor = Math.Max(0.1f, Math.Min(5.0f, value)); Invalidate(); } } /// /// 선택된 노드 /// public MapNode SelectedNode => _selectedNode; /// /// 노드 목록 /// public List Nodes { get => _nodes ?? new List(); set { _nodes = value ?? new List(); Invalidate(); } } /// /// AGV 목록 /// public List AGVList { get => _agvList ?? new List(); set { _agvList = value ?? new List(); UpdateAGVData(); Invalidate(); } } /// /// 현재 표시할 경로 /// public PathResult CurrentPath { get => _currentPath; set { _currentPath = value; Invalidate(); } } /// /// 모든 경로 목록 (다중 AGV 경로 표시용) /// public List AllPaths { get => _allPaths ?? new List(); set { _allPaths = value ?? new List(); Invalidate(); } } /// /// 회사 로고 이미지 /// public Image CompanyLogo { get => _companyLogo; set { _companyLogo = value; Invalidate(); } } /// /// 측정 정보 텍스트 /// public string MeasurementInfo { get => _measurementInfo; set { _measurementInfo = value; Invalidate(); } } #endregion #region Constructor public UnifiedAGVCanvas() { InitializeComponent(); InitializeCanvas(); } #endregion #region Initialization private void InitializeCanvas() { SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.ResizeRedraw, true); _nodes = new List(); _agvList = new List(); _agvPositions = new Dictionary(); _agvDirections = new Dictionary(); _agvStates = new Dictionary(); _allPaths = new List(); InitializeBrushesAndPens(); CreateContextMenu(); } private void InitializeBrushesAndPens() { // 노드 브러쉬 _normalNodeBrush = new SolidBrush(Color.LightBlue); _rotationNodeBrush = new SolidBrush(Color.Yellow); _dockingNodeBrush = new SolidBrush(Color.Orange); _chargingNodeBrush = new SolidBrush(Color.Green); _selectedNodeBrush = new SolidBrush(Color.Red); _hoveredNodeBrush = new SolidBrush(Color.LightCyan); // AGV 및 경로 브러쉬 _agvBrush = new SolidBrush(Color.Red); _pathBrush = new SolidBrush(Color.Purple); // 그리드 브러쉬 _gridBrush = new SolidBrush(Color.LightGray); // 펜 _connectionPen = new Pen(Color.DarkBlue, CONNECTION_WIDTH); _connectionPen.EndCap = LineCap.ArrowAnchor; _gridPen = new Pen(Color.LightGray, 1); _tempConnectionPen = new Pen(Color.Orange, 2) { DashStyle = DashStyle.Dash }; _selectedNodePen = new Pen(Color.Red, 3); _pathPen = new Pen(Color.Purple, 3); _agvPen = new Pen(Color.Red, 3); } private void CreateContextMenu() { _contextMenu = new ContextMenuStrip(); // 컨텍스트 메뉴는 EditMode에서만 사용 } private void UpdateModeUI() { // 모드에 따른 UI 업데이트 if (_canvasMode == CanvasMode.ViewOnly) { Cursor = Cursors.Default; _contextMenu.Enabled = false; } else { _contextMenu.Enabled = true; Cursor = GetCursorForMode(_editMode); } } #endregion #region AGV Management /// /// AGV 위치 업데이트 /// public void UpdateAGVPosition(string agvId, Point position) { if (_agvPositions.ContainsKey(agvId)) _agvPositions[agvId] = position; else _agvPositions.Add(agvId, position); Invalidate(); } /// /// AGV 방향 업데이트 /// public void UpdateAGVDirection(string agvId, AgvDirection direction) { if (_agvDirections.ContainsKey(agvId)) _agvDirections[agvId] = direction; else _agvDirections.Add(agvId, direction); Invalidate(); } /// /// AGV 상태 업데이트 /// public void UpdateAGVState(string agvId, AGVState state) { if (_agvStates.ContainsKey(agvId)) _agvStates[agvId] = state; else _agvStates.Add(agvId, state); Invalidate(); } /// /// AGV 위치 설정 (시뮬레이터용) /// /// AGV ID /// 새로운 위치 public void SetAGVPosition(string agvId, Point position) { UpdateAGVPosition(agvId, position); } /// /// AGV 데이터 동기화 /// private void UpdateAGVData() { if (_agvList == null) return; foreach (var agv in _agvList) { UpdateAGVPosition(agv.AgvId, agv.CurrentPosition); UpdateAGVDirection(agv.AgvId, agv.CurrentDirection); UpdateAGVState(agv.AgvId, agv.CurrentState); } } #endregion #region Helper Methods private Cursor GetCursorForMode(EditMode mode) { if (_canvasMode != CanvasMode.Edit) return Cursors.Default; switch (mode) { case EditMode.AddNode: return Cursors.Cross; case EditMode.Move: return Cursors.SizeAll; case EditMode.Connect: return Cursors.Hand; case EditMode.Delete: return Cursors.No; default: return Cursors.Default; } } private void CancelConnection() { _isConnectionMode = false; _connectionStartNode = null; _connectionEndPoint = Point.Empty; Invalidate(); } #endregion #region Cleanup protected override void Dispose(bool disposing) { if (disposing) { // 브러쉬 정리 _normalNodeBrush?.Dispose(); _rotationNodeBrush?.Dispose(); _dockingNodeBrush?.Dispose(); _chargingNodeBrush?.Dispose(); _selectedNodeBrush?.Dispose(); _hoveredNodeBrush?.Dispose(); _gridBrush?.Dispose(); _agvBrush?.Dispose(); _pathBrush?.Dispose(); // 펜 정리 _connectionPen?.Dispose(); _gridPen?.Dispose(); _tempConnectionPen?.Dispose(); _selectedNodePen?.Dispose(); _pathPen?.Dispose(); _agvPen?.Dispose(); // 컨텍스트 메뉴 정리 _contextMenu?.Dispose(); // 이미지 정리 _companyLogo?.Dispose(); } base.Dispose(disposing); } #endregion } #region Interfaces /// /// AGV 인터페이스 (가상/실제 AGV 통합) /// public interface IAGV { string AgvId { get; } Point CurrentPosition { get; } AgvDirection CurrentDirection { get; } AGVState CurrentState { get; } float BatteryLevel { get; } } /// /// AGV 상태 열거형 /// public enum AGVState { Idle, // 대기 Moving, // 이동 중 Rotating, // 회전 중 Docking, // 도킹 중 Charging, // 충전 중 Error // 오류 } #endregion }