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 = 40;
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;
private MapNode _destinationNode;
// 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 _destinationNodeBrush;
private Brush _gridBrush;
private Brush _agvBrush;
private Brush _pathBrush;
private Pen _connectionPen;
private Pen _gridPen;
private Pen _tempConnectionPen;
private Pen _selectedNodePen;
private Pen _destinationNodePen;
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;
UpdateDestinationNode();
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);
_destinationNodeBrush = new SolidBrush(Color.Gold);
// 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);
_destinationNodePen = new Pen(Color.Orange, 4);
_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();
}
private void UpdateDestinationNode()
{
_destinationNode = null;
if (_currentPath != null && _currentPath.Success && _currentPath.Path != null && _currentPath.Path.Count > 0)
{
// 경로의 마지막 노드가 목적지
string destinationNodeId = _currentPath.Path[_currentPath.Path.Count - 1];
// 노드 목록에서 해당 노드 찾기
_destinationNode = _nodes?.FirstOrDefault(n => n.NodeId == destinationNodeId);
}
}
#endregion
#region Cleanup
protected override void Dispose(bool disposing)
{
if (disposing)
{
// 브러쉬 정리
_normalNodeBrush?.Dispose();
_rotationNodeBrush?.Dispose();
_dockingNodeBrush?.Dispose();
_chargingNodeBrush?.Dispose();
_selectedNodeBrush?.Dispose();
_hoveredNodeBrush?.Dispose();
_destinationNodeBrush?.Dispose();
_gridBrush?.Dispose();
_agvBrush?.Dispose();
_pathBrush?.Dispose();
// 펜 정리
_connectionPen?.Dispose();
_gridPen?.Dispose();
_tempConnectionPen?.Dispose();
_selectedNodePen?.Dispose();
_destinationNodePen?.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; }
// 이동 경로 정보 추가
Point? TargetPosition { get; }
string CurrentNodeId { get; }
string TargetNodeId { get; }
DockingDirection DockingDirection { get; }
}
///
/// AGV 상태 열거형
///
public enum AGVState
{
Idle, // 대기
Moving, // 이동 중
Rotating, // 회전 중
Docking, // 도킹 중
Charging, // 충전 중
Error // 오류
}
#endregion
}