Files
ENIG/Cs_HMI/AGVLogic/AGVSimulator/Models/VirtualAGV.cs
backuppc dbaf647d4e refactor: Move AGV development projects to separate AGVLogic folder
- Reorganized AGVMapEditor, AGVNavigationCore, AGVSimulator into AGVLogic folder
- Removed deleted project files from root folder tracking
- Updated CLAUDE.md with AGVLogic-specific development guidelines
- Clean separation of independent project development from main codebase
- Projects now ready for independent development and future integration

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 10:00:40 +09:00

561 lines
18 KiB
C#

//using System;
//using System.Collections.Generic;
//using System.Drawing;
//using System.Linq;
//using AGVMapEditor.Models;
//using AGVNavigationCore.Models;
//using AGVNavigationCore.PathFinding;
//using AGVNavigationCore.PathFinding.Core;
//using AGVNavigationCore.Controls;
//namespace AGVSimulator.Models
//{
// /// <summary>
// /// 가상 AGV 클래스
// /// 실제 AGV의 동작을 시뮬레이션
// /// </summary>
// public class VirtualAGV : IAGV
// {
// #region Events
// /// <summary>
// /// AGV 상태 변경 이벤트
// /// </summary>
// public event EventHandler<AGVState> StateChanged;
// /// <summary>
// /// 위치 변경 이벤트
// /// </summary>
// public event EventHandler<(Point, AgvDirection, MapNode)> PositionChanged;
// /// <summary>
// /// RFID 감지 이벤트
// /// </summary>
// public event EventHandler<string> RfidDetected;
// /// <summary>
// /// 경로 완료 이벤트
// /// </summary>
// public event EventHandler<AGVPathResult> PathCompleted;
// /// <summary>
// /// 오류 발생 이벤트
// /// </summary>
// public event EventHandler<string> ErrorOccurred;
// #endregion
// #region Fields
// private string _agvId;
// private Point _currentPosition;
// private Point _targetPosition;
// private string _targetId;
// private string _currentId;
// private AgvDirection _currentDirection;
// private AgvDirection _targetDirection;
// private AGVState _currentState;
// private float _currentSpeed;
// // 경로 관련
// private AGVPathResult _currentPath;
// private List<string> _remainingNodes;
// private int _currentNodeIndex;
// private MapNode _currentNode;
// private MapNode _targetNode;
// // 이동 관련
// private System.Windows.Forms.Timer _moveTimer;
// private DateTime _lastMoveTime;
// private Point _moveStartPosition;
// private Point _moveTargetPosition;
// private float _moveProgress;
// // 도킹 관련
// private DockingDirection _dockingDirection;
// // 시뮬레이션 설정
// private readonly float _moveSpeed = 50.0f; // 픽셀/초
// private readonly float _rotationSpeed = 90.0f; // 도/초
// private readonly int _updateInterval = 50; // ms
// #endregion
// #region Properties
// /// <summary>
// /// AGV ID
// /// </summary>
// public string AgvId => _agvId;
// /// <summary>
// /// 현재 위치
// /// </summary>
// public Point CurrentPosition
// {
// get => _currentPosition;
// set => _currentPosition = value;
// }
// /// <summary>
// /// 현재 방향
// /// 모터의 동작 방향
// /// </summary>
// public AgvDirection CurrentDirection
// {
// get => _currentDirection;
// set => _currentDirection = value;
// }
// /// <summary>
// /// 현재 상태
// /// </summary>
// public AGVState CurrentState
// {
// get => _currentState;
// set => _currentState = value;
// }
// /// <summary>
// /// 현재 속도
// /// </summary>
// public float CurrentSpeed => _currentSpeed;
// /// <summary>
// /// 현재 경로
// /// </summary>
// public AGVPathResult CurrentPath => _currentPath;
// /// <summary>
// /// 현재 노드 ID
// /// </summary>
// public string CurrentNodeId => _currentNode.NodeId;
// /// <summary>
// /// 목표 위치
// /// </summary>
// public Point? TargetPosition => _targetPosition;
// /// <summary>
// /// 배터리 레벨 (시뮬레이션)
// /// </summary>
// public float BatteryLevel { get; set; } = 100.0f;
// /// <summary>
// /// 목표 노드 ID
// /// </summary>
// public string TargetNodeId => _targetNode.NodeId;
// /// <summary>
// /// 도킹 방향
// /// </summary>
// public DockingDirection DockingDirection => _dockingDirection;
// #endregion
// #region Constructor
// /// <summary>
// /// 생성자
// /// </summary>
// /// <param name="agvId">AGV ID</param>
// /// <param name="startPosition">시작 위치</param>
// /// <param name="startDirection">시작 방향</param>
// public VirtualAGV(string agvId, Point startPosition, AgvDirection startDirection = AgvDirection.Forward)
// {
// _agvId = agvId;
// _currentPosition = startPosition;
// _currentDirection = startDirection;
// _currentState = AGVState.Idle;
// _currentSpeed = 0;
// _dockingDirection = DockingDirection.Forward; // 기본값: 전진 도킹
// _currentNode = null; // = string.Empty;
// _targetNode = null;// string.Empty;
// InitializeTimer();
// }
// #endregion
// #region Initialization
// private void InitializeTimer()
// {
// _moveTimer = new System.Windows.Forms.Timer();
// _moveTimer.Interval = _updateInterval;
// _moveTimer.Tick += OnMoveTimer_Tick;
// _lastMoveTime = DateTime.Now;
// }
// #endregion
// #region Public Methods
// /// <summary>
// /// 경로 실행 시작
// /// </summary>
// /// <param name="path">실행할 경로</param>
// /// <param name="mapNodes">맵 노드 목록</param>
// public void StartPath(AGVPathResult path, List<MapNode> mapNodes)
// {
// if (path == null || !path.Success)
// {
// OnError("유효하지 않은 경로입니다.");
// return;
// }
// _currentPath = path;
// _remainingNodes = new List<string>(path.Path);
// _currentNodeIndex = 0;
// // 시작 노드와 목표 노드 설정
// if (_remainingNodes.Count > 0)
// {
// var startNode = mapNodes.FirstOrDefault(n => n.NodeId == _remainingNodes[0]);
// if (startNode != null)
// {
// _currentNode = startNode;
// // 목표 노드 설정 (경로의 마지막 노드)
// if (_remainingNodes.Count > 1)
// {
// var _targetNodeId = _remainingNodes[_remainingNodes.Count - 1];
// var targetNode = mapNodes.FirstOrDefault(n => n.NodeId == _targetNodeId);
// // 목표 노드의 타입에 따라 도킹 방향 결정
// if (targetNode != null)
// {
// _dockingDirection = GetDockingDirection(targetNode.Type);
// }
// }
// StartMovement();
// }
// else
// {
// OnError($"시작 노드를 찾을 수 없습니다: {_remainingNodes[0]}");
// }
// }
// }
// /// <summary>
// /// 경로 정지
// /// </summary>
// public void StopPath()
// {
// _moveTimer.Stop();
// _currentPath = null;
// _remainingNodes?.Clear();
// SetState(AGVState.Idle);
// _currentSpeed = 0;
// }
// /// <summary>
// /// 긴급 정지
// /// </summary>
// public void EmergencyStop()
// {
// StopPath();
// OnError("긴급 정지가 실행되었습니다.");
// }
// /// <summary>
// /// 수동 이동 (테스트용)
// /// </summary>
// /// <param name="targetPosition">목표 위치</param>
// public void MoveTo(Point targetPosition)
// {
// _targetPosition = targetPosition;
// _moveStartPosition = _currentPosition;
// _moveTargetPosition = targetPosition;
// _moveProgress = 0;
// SetState(AGVState.Moving);
// _moveTimer.Start();
// }
// /// <summary>
// /// 수동 회전 (테스트용)
// /// </summary>
// /// <param name="direction">회전 방향</param>
// public void Rotate(AgvDirection direction)
// {
// if (_currentState != AGVState.Idle)
// return;
// SetState(AGVState.Rotating);
// // 시뮬레이션: 즉시 방향 변경 (실제로는 시간이 걸림)
// _currentDirection = direction;
// System.Threading.Thread.Sleep(500); // 회전 시간 시뮬레이션
// SetState(AGVState.Idle);
// }
// /// <summary>
// /// AGV 위치 직접 설정 (시뮬레이터용)
// /// TargetPosition을 이전 위치로 저장하여 리프트 방향 계산이 가능하도록 함
// /// </summary>
// /// <param name="newPosition">새로운 위치</param>
// /// <param name="motorDirection">모터이동방향</param>
// public void SetPosition(MapNode node, Point newPosition, AgvDirection motorDirection)
// {
// // 현재 위치를 이전 위치로 저장 (리프트 방향 계산용)
// if (_currentPosition != Point.Empty)
// {
// _targetPosition = _currentPosition; // 이전 위치 (previousPos 역할)
// _targetDirection = _currentDirection;
// _targetNode = node;
// }
// // 새로운 위치 설정
// _currentPosition = newPosition;
// _currentDirection = motorDirection;
// _currentNode = node;
// // 위치 변경 이벤트 발생
// PositionChanged?.Invoke(this, (_currentPosition, _currentDirection, _currentNode));
// }
// /// <summary>
// /// 충전 시작 (시뮬레이션)
// /// </summary>
// public void StartCharging()
// {
// if (_currentState == AGVState.Idle)
// {
// SetState(AGVState.Charging);
// // 충전 시뮬레이션 시작
// }
// }
// /// <summary>
// /// 충전 종료
// /// </summary>
// public void StopCharging()
// {
// if (_currentState == AGVState.Charging)
// {
// SetState(AGVState.Idle);
// }
// }
// /// <summary>
// /// AGV 정보 조회
// /// </summary>
// public string GetStatus()
// {
// return $"AGV[{_agvId}] 위치:({_currentPosition.X},{_currentPosition.Y}) " +
// $"방향:{_currentDirection} 상태:{_currentState} " +
// $"속도:{_currentSpeed:F1} 배터리:{BatteryLevel:F1}%";
// }
// /// <summary>
// /// 현재 RFID 시뮬레이션 (현재 위치 기준)
// /// </summary>
// public string SimulateRfidReading(List<MapNode> mapNodes)
// {
// // 현재 위치에서 가장 가까운 노드 찾기
// var closestNode = FindClosestNode(_currentPosition, mapNodes);
// if (closestNode == null)
// return null;
// // 해당 노드의 RFID 정보 반환 (MapNode에 RFID 정보 포함)
// return closestNode.HasRfid() ? closestNode.RfidId : null;
// }
// #endregion
// #region Private Methods
// private void StartMovement()
// {
// SetState(AGVState.Moving);
// _moveTimer.Start();
// _lastMoveTime = DateTime.Now;
// }
// private void OnMoveTimer_Tick(object sender, EventArgs e)
// {
// var now = DateTime.Now;
// var deltaTime = (float)(now - _lastMoveTime).TotalSeconds;
// _lastMoveTime = now;
// UpdateMovement(deltaTime);
// UpdateBattery(deltaTime);
// // 위치 변경 이벤트 발생
// PositionChanged?.Invoke(this, (_currentPosition, _currentDirection, _currentNode));
// }
// private void UpdateMovement(float deltaTime)
// {
// if (_currentState != AGVState.Moving)
// return;
// // 목표 위치까지의 거리 계산
// var distance = CalculateDistance(_currentPosition, _moveTargetPosition);
// if (distance < 5.0f) // 도달 임계값
// {
// // 목표 도달
// _currentPosition = _moveTargetPosition;
// _currentSpeed = 0;
// // 다음 노드로 이동
// ProcessNextNode();
// }
// else
// {
// // 계속 이동
// var moveDistance = _moveSpeed * deltaTime;
// var direction = new PointF(
// _moveTargetPosition.X - _currentPosition.X,
// _moveTargetPosition.Y - _currentPosition.Y
// );
// // 정규화
// var length = (float)Math.Sqrt(direction.X * direction.X + direction.Y * direction.Y);
// if (length > 0)
// {
// direction.X /= length;
// direction.Y /= length;
// }
// // 새 위치 계산
// _currentPosition = new Point(
// (int)(_currentPosition.X + direction.X * moveDistance),
// (int)(_currentPosition.Y + direction.Y * moveDistance)
// );
// _currentSpeed = _moveSpeed;
// }
// }
// private void UpdateBattery(float deltaTime)
// {
// // 배터리 소모 시뮬레이션
// if (_currentState == AGVState.Moving)
// {
// BatteryLevel -= 0.1f * deltaTime; // 이동시 소모
// }
// else if (_currentState == AGVState.Charging)
// {
// BatteryLevel += 5.0f * deltaTime; // 충전
// BatteryLevel = Math.Min(100.0f, BatteryLevel);
// }
// BatteryLevel = Math.Max(0, BatteryLevel);
// // 배터리 부족 경고
// if (BatteryLevel < 20.0f && _currentState != AGVState.Charging)
// {
// OnError($"배터리 부족: {BatteryLevel:F1}%");
// }
// }
// private void ProcessNextNode()
// {
// if (_remainingNodes == null || _currentNodeIndex >= _remainingNodes.Count - 1)
// {
// // 경로 완료
// _moveTimer.Stop();
// SetState(AGVState.Idle);
// PathCompleted?.Invoke(this, _currentPath);
// return;
// }
// // 다음 노드로 이동
// _currentNodeIndex++;
// var nextNodeId = _remainingNodes[_currentNodeIndex];
// // RFID 감지 시뮬레이션
// RfidDetected?.Invoke(this, $"RFID_{nextNodeId}");
// //_currentNodeId = nextNodeId;
// // 다음 목표 위치 설정 (실제로는 맵에서 좌표 가져와야 함)
// // 여기서는 간단히 현재 위치에서 랜덤 오프셋으로 설정
// var random = new Random();
// _moveTargetPosition = new Point(
// _currentPosition.X + random.Next(-100, 100),
// _currentPosition.Y + random.Next(-100, 100)
// );
// }
// private MapNode FindClosestNode(Point position, List<MapNode> mapNodes)
// {
// if (mapNodes == null || mapNodes.Count == 0)
// return null;
// MapNode closestNode = null;
// float closestDistance = float.MaxValue;
// foreach (var node in mapNodes)
// {
// var distance = CalculateDistance(position, node.Position);
// if (distance < closestDistance)
// {
// closestDistance = distance;
// closestNode = node;
// }
// }
// // 일정 거리 내에 있는 노드만 반환
// return closestDistance < 50.0f ? closestNode : null;
// }
// private float CalculateDistance(Point from, Point to)
// {
// var dx = to.X - from.X;
// var dy = to.Y - from.Y;
// return (float)Math.Sqrt(dx * dx + dy * dy);
// }
// private void SetState(AGVState newState)
// {
// if (_currentState != newState)
// {
// _currentState = newState;
// StateChanged?.Invoke(this, newState);
// }
// }
// private DockingDirection GetDockingDirection(NodeType nodeType)
// {
// switch (nodeType)
// {
// case NodeType.Charging:
// return DockingDirection.Forward; // 충전기: 전진 도킹
// case NodeType.Docking:
// return DockingDirection.Backward; // 장비 (로더, 클리너, 오프로더, 버퍼): 후진 도킹
// default:
// return DockingDirection.Forward; // 기본값: 전진
// }
// }
// private void OnError(string message)
// {
// SetState(AGVState.Error);
// ErrorOccurred?.Invoke(this, message);
// }
// #endregion
// #region Cleanup
// /// <summary>
// /// 리소스 정리
// /// </summary>
// public void Dispose()
// {
// _moveTimer?.Stop();
// _moveTimer?.Dispose();
// }
// #endregion
// }
//}