feat: Implement AGV command prediction system for real-time control
- Add motor/magnet/speed enums and AGVCommand class for AGV control - Implement Predict() method for next action prediction based on path and state - Add RFID position tracking (requires 2 RFIDs for position confirmation) - Add SetPath() method to VirtualAGV for path management - Implement GetCommandFromPath() to extract motor/magnet/speed from DetailedPath - Add real-time prediction display in SimulatorForm (timer1_Tick) - Support automatic forward movement at low speed when position unconfirmed - Support stop command when destination reached or no destination set Key Features: - Position unconfirmed (RFID < 2): Forward + Straight + Low speed - Position confirmed + no destination: Stop (position found) - Position confirmed + destination reached: Stop (arrived) - Path execution: Motor/Magnet/Speed from DetailedPath NodeMotorInfo - Rotation nodes: Automatic low speed - Junction handling: Magnet direction (Left/Right/Straight) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -69,4 +69,90 @@ namespace AGVNavigationCore.Models
|
|||||||
/// <summary>충전기</summary>
|
/// <summary>충전기</summary>
|
||||||
Charger
|
Charger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 모터 명령 열거형 (실제 AGV 제어용)
|
||||||
|
/// </summary>
|
||||||
|
public enum MotorCommand
|
||||||
|
{
|
||||||
|
/// <summary>정지</summary>
|
||||||
|
Stop,
|
||||||
|
/// <summary>전진 (Forward - 모니터 방향)</summary>
|
||||||
|
Forward,
|
||||||
|
/// <summary>후진 (Backward - 리프트 방향)</summary>
|
||||||
|
Backward
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 마그넷 위치 열거형 (실제 AGV 제어용)
|
||||||
|
/// </summary>
|
||||||
|
public enum MagnetPosition
|
||||||
|
{
|
||||||
|
/// <summary>직진 (Straight)</summary>
|
||||||
|
S,
|
||||||
|
/// <summary>왼쪽 (Left)</summary>
|
||||||
|
L,
|
||||||
|
/// <summary>오른쪽 (Right)</summary>
|
||||||
|
R
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 속도 레벨 열거형 (실제 AGV 제어용)
|
||||||
|
/// </summary>
|
||||||
|
public enum SpeedLevel
|
||||||
|
{
|
||||||
|
/// <summary>저속 (Low)</summary>
|
||||||
|
L,
|
||||||
|
/// <summary>중속 (Medium)</summary>
|
||||||
|
M,
|
||||||
|
/// <summary>고속 (High)</summary>
|
||||||
|
H
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AGV 제어 명령 클래스 (실제 AGV 제어용)
|
||||||
|
/// Predict() 메서드가 반환하는 다음 동작 명령
|
||||||
|
/// </summary>
|
||||||
|
public class AGVCommand
|
||||||
|
{
|
||||||
|
/// <summary>모터 명령 (정지/전진/후진)</summary>
|
||||||
|
public MotorCommand Motor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>마그넷 위치 (직진/왼쪽/오른쪽)</summary>
|
||||||
|
public MagnetPosition Magnet { get; set; }
|
||||||
|
|
||||||
|
/// <summary>속도 레벨 (저속/중속/고속)</summary>
|
||||||
|
public SpeedLevel Speed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>명령 이유 (디버깅/로깅용)</summary>
|
||||||
|
public string Reason { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 생성자
|
||||||
|
/// </summary>
|
||||||
|
public AGVCommand(MotorCommand motor, MagnetPosition magnet, SpeedLevel speed, string reason = "")
|
||||||
|
{
|
||||||
|
Motor = motor;
|
||||||
|
Magnet = magnet;
|
||||||
|
Speed = speed;
|
||||||
|
Reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 기본 생성자
|
||||||
|
/// </summary>
|
||||||
|
public AGVCommand()
|
||||||
|
{
|
||||||
|
Motor = MotorCommand.Stop;
|
||||||
|
Magnet = MagnetPosition.S;
|
||||||
|
Speed = SpeedLevel.L;
|
||||||
|
Reason = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"Motor:{Motor}, Magnet:{Magnet}, Speed:{Speed}" +
|
||||||
|
(string.IsNullOrEmpty(Reason) ? "" : $" ({Reason})");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -75,6 +75,10 @@ namespace AGVNavigationCore.Models
|
|||||||
private readonly float _moveSpeed = 50.0f; // 픽셀/초
|
private readonly float _moveSpeed = 50.0f; // 픽셀/초
|
||||||
private bool _isMoving;
|
private bool _isMoving;
|
||||||
|
|
||||||
|
// RFID 위치 추적 (실제 AGV용)
|
||||||
|
private List<string> _detectedRfids = new List<string>(); // 감지된 RFID 목록
|
||||||
|
private bool _isPositionConfirmed = false; // 위치 확정 여부 (RFID 2개 이상 감지)
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
@@ -159,6 +163,16 @@ namespace AGVNavigationCore.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DockingDirection DockingDirection => _dockingDirection;
|
public DockingDirection DockingDirection => _dockingDirection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 위치 확정 여부 (RFID 2개 이상 감지 시 true)
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPositionConfirmed => _isPositionConfirmed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 감지된 RFID 개수
|
||||||
|
/// </summary>
|
||||||
|
public int DetectedRfidCount => _detectedRfids.Count;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructor
|
#region Constructor
|
||||||
@@ -200,6 +214,18 @@ namespace AGVNavigationCore.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetDetectedRfid(string rfidId)
|
public void SetDetectedRfid(string rfidId)
|
||||||
{
|
{
|
||||||
|
// RFID 목록에 추가 (중복 제거)
|
||||||
|
if (!_detectedRfids.Contains(rfidId))
|
||||||
|
{
|
||||||
|
_detectedRfids.Add(rfidId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFID 2개 이상 감지 시 위치 확정
|
||||||
|
if (_detectedRfids.Count >= 2 && !_isPositionConfirmed)
|
||||||
|
{
|
||||||
|
_isPositionConfirmed = true;
|
||||||
|
}
|
||||||
|
|
||||||
RfidDetected?.Invoke(this, rfidId);
|
RfidDetected?.Invoke(this, rfidId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,6 +251,172 @@ namespace AGVNavigationCore.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 다음 동작 예측 (실제 AGV 제어용)
|
||||||
|
/// AGV가 지속적으로 호출하여 현재 상태와 예측 상태를 일치시킴
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>다음에 수행할 모터/마그넷/속도 명령</returns>
|
||||||
|
public AGVCommand Predict()
|
||||||
|
{
|
||||||
|
// 1. 위치 미확정 상태 (RFID 2개 미만 감지)
|
||||||
|
if (!_isPositionConfirmed)
|
||||||
|
{
|
||||||
|
// 항상 전진 + 저속으로 이동 (RFID 감지 대기)
|
||||||
|
return new AGVCommand(
|
||||||
|
MotorCommand.Forward,
|
||||||
|
MagnetPosition.S, // 직진
|
||||||
|
SpeedLevel.L, // 저속
|
||||||
|
$"위치 미확정 (RFID {_detectedRfids.Count}/2) - 전진하여 RFID 탐색"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 위치 확정됨 + 경로 없음 → 정지 (목적지 미설정 상태)
|
||||||
|
if (_currentPath == null || (_currentPath.DetailedPath?.Count ?? 0) < 1)
|
||||||
|
{
|
||||||
|
return new AGVCommand(
|
||||||
|
MotorCommand.Stop,
|
||||||
|
MagnetPosition.S,
|
||||||
|
SpeedLevel.L,
|
||||||
|
$"위치 확정 완료 (목적지 미설정) - 현재:{_currentNode?.NodeId ?? "알수없음"}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 위치 확정됨 + 경로 있음 + 남은 노드 없음 → 정지 (목적지 도착)
|
||||||
|
var lastNode = _currentPath.DetailedPath.Last();
|
||||||
|
if (_currentPath.DetailedPath.Where(t => t.seq < lastNode.seq && t.IsPass == false).Any() == false)
|
||||||
|
{
|
||||||
|
return new AGVCommand(
|
||||||
|
MotorCommand.Stop,
|
||||||
|
MagnetPosition.S,
|
||||||
|
SpeedLevel.L,
|
||||||
|
$"목적지 도착 - 최종:{_currentNode?.NodeId ?? "알수없음"}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 경로이탈
|
||||||
|
var TargetNode = _currentPath.DetailedPath.Where(t => t.IsPass == false && t.NodeId.Equals(_currentNode.NodeId)).FirstOrDefault();
|
||||||
|
if (TargetNode == null)
|
||||||
|
{
|
||||||
|
return new AGVCommand(
|
||||||
|
MotorCommand.Stop,
|
||||||
|
MagnetPosition.S,
|
||||||
|
SpeedLevel.L,
|
||||||
|
$"(재탐색요청)경로이탈 현재위치:{_currentNode.NodeId}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 방향체크
|
||||||
|
if(CurrentDirection != TargetNode.MotorDirection)
|
||||||
|
{
|
||||||
|
return new AGVCommand(
|
||||||
|
MotorCommand.Stop,
|
||||||
|
MagnetPosition.S,
|
||||||
|
SpeedLevel.L,
|
||||||
|
$"(재탐색요청)모터방향 불일치 현재위치:{_currentNode.NodeId}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//this.CurrentNodeId
|
||||||
|
|
||||||
|
return GetCommandFromPath(CurrentNodeId, "경로 실행 시작");
|
||||||
|
|
||||||
|
// 4. 위치 확정 + 경로 실행 중 → 현재 상태에 따른 명령 예측
|
||||||
|
switch (_currentState)
|
||||||
|
{
|
||||||
|
case AGVState.Idle:
|
||||||
|
// 🔥 경로가 있다면 이동 시작 (경로 실행 대기 중)
|
||||||
|
if (_currentPath != null && _remainingNodes != null && _remainingNodes.Count > 0)
|
||||||
|
{
|
||||||
|
// DetailedPath에서 다음 노드 정보 가져오기
|
||||||
|
return GetCommandFromPath(_remainingNodes[0], "경로 실행 시작");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 경로가 없으면 대기
|
||||||
|
return new AGVCommand(
|
||||||
|
MotorCommand.Stop,
|
||||||
|
MagnetPosition.S,
|
||||||
|
SpeedLevel.L,
|
||||||
|
"대기 중 (경로 없음)"
|
||||||
|
);
|
||||||
|
|
||||||
|
case AGVState.Moving:
|
||||||
|
{
|
||||||
|
// 이동 중 - DetailedPath에서 현재/다음 노드 정보 가져오기
|
||||||
|
if (_currentPath != null && _remainingNodes != null && _remainingNodes.Count > 0)
|
||||||
|
{
|
||||||
|
return GetCommandFromPath(_remainingNodes[0], "이동 중");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 경로 정보가 없으면 기본 명령 (fallback)
|
||||||
|
var motorCmd = _currentDirection == AgvDirection.Forward
|
||||||
|
? MotorCommand.Forward
|
||||||
|
: MotorCommand.Backward;
|
||||||
|
|
||||||
|
return new AGVCommand(
|
||||||
|
motorCmd,
|
||||||
|
MagnetPosition.S,
|
||||||
|
SpeedLevel.M,
|
||||||
|
$"이동 중 (DetailedPath 없음)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
case AGVState.Rotating:
|
||||||
|
// 회전 중 - 정지 상태에서 마그넷만 조정
|
||||||
|
MagnetPosition magnetPos = MagnetPosition.S;
|
||||||
|
if (_currentDirection == AgvDirection.Left)
|
||||||
|
magnetPos = MagnetPosition.L;
|
||||||
|
else if (_currentDirection == AgvDirection.Right)
|
||||||
|
magnetPos = MagnetPosition.R;
|
||||||
|
|
||||||
|
return new AGVCommand(
|
||||||
|
MotorCommand.Stop, // 회전은 정지 상태에서
|
||||||
|
magnetPos,
|
||||||
|
SpeedLevel.L,
|
||||||
|
$"회전 중 ({_currentDirection})"
|
||||||
|
);
|
||||||
|
|
||||||
|
case AGVState.Docking:
|
||||||
|
{
|
||||||
|
// 도킹 중 - 저속으로 전진 또는 후진
|
||||||
|
var dockingMotor = _dockingDirection == DockingDirection.Forward
|
||||||
|
? MotorCommand.Forward
|
||||||
|
: MotorCommand.Backward;
|
||||||
|
|
||||||
|
return new AGVCommand(
|
||||||
|
dockingMotor,
|
||||||
|
MagnetPosition.S,
|
||||||
|
SpeedLevel.L, // 도킹은 항상 저속
|
||||||
|
$"도킹 중 ({_dockingDirection})"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
case AGVState.Charging:
|
||||||
|
return new AGVCommand(
|
||||||
|
MotorCommand.Stop,
|
||||||
|
MagnetPosition.S,
|
||||||
|
SpeedLevel.L,
|
||||||
|
"충전 중"
|
||||||
|
);
|
||||||
|
|
||||||
|
case AGVState.Error:
|
||||||
|
return new AGVCommand(
|
||||||
|
MotorCommand.Stop,
|
||||||
|
MagnetPosition.S,
|
||||||
|
SpeedLevel.L,
|
||||||
|
"오류 발생"
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return new AGVCommand(
|
||||||
|
MotorCommand.Stop,
|
||||||
|
MagnetPosition.S,
|
||||||
|
SpeedLevel.L,
|
||||||
|
"알 수 없는 상태"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Public Methods - 상태 조회
|
#region Public Methods - 상태 조회
|
||||||
@@ -258,7 +450,28 @@ namespace AGVNavigationCore.Models
|
|||||||
|
|
||||||
#region Public Methods - 경로 실행
|
#region Public Methods - 경로 실행
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 경로 설정 (실제 AGV 및 시뮬레이터에서 사용)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">실행할 경로</param>
|
||||||
|
public void SetPath(AGVPathResult path)
|
||||||
|
{
|
||||||
|
if (path == null)
|
||||||
|
{
|
||||||
|
OnError("경로가 null입니다.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentPath = path;
|
||||||
|
_remainingNodes = path.Path.Select(n => n.NodeId).ToList(); // MapNode → NodeId 변환
|
||||||
|
_currentNodeIndex = 0;
|
||||||
|
|
||||||
|
// 경로 시작 노드가 현재 노드와 다른 경우 경고
|
||||||
|
if (_currentNode != null && _remainingNodes.Count > 0 && _remainingNodes[0] != _currentNode.NodeId)
|
||||||
|
{
|
||||||
|
OnError($"경로 시작 노드({_remainingNodes[0]})와 현재 노드({_currentNode.NodeId})가 다릅니다.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 경로 정지
|
/// 경로 정지
|
||||||
@@ -388,6 +601,31 @@ namespace AGVNavigationCore.Models
|
|||||||
_currentDirection = motorDirection;
|
_currentDirection = motorDirection;
|
||||||
_currentNode = node;
|
_currentNode = node;
|
||||||
|
|
||||||
|
// 🔥 노드 ID를 RFID로 간주하여 감지 목록에 추가 (시뮬레이터용)
|
||||||
|
if (!string.IsNullOrEmpty(node.NodeId) && !_detectedRfids.Contains(node.NodeId))
|
||||||
|
{
|
||||||
|
_detectedRfids.Add(node.NodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔥 RFID 2개 이상 감지 시 위치 확정
|
||||||
|
if (_detectedRfids.Count >= 2 && !_isPositionConfirmed)
|
||||||
|
{
|
||||||
|
_isPositionConfirmed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//현재 경로값이 있는지 확인한다.
|
||||||
|
if (CurrentPath != null && CurrentPath.DetailedPath != null && CurrentPath.DetailedPath.Any())
|
||||||
|
{
|
||||||
|
var item = CurrentPath.DetailedPath.FirstOrDefault(t => t.NodeId == node.NodeId && t.IsPass == false);
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
//item.IsPass = true;
|
||||||
|
|
||||||
|
//이전노드는 모두 지나친걸로 한다
|
||||||
|
CurrentPath.DetailedPath.Where(t => t.seq < item.seq).ToList().ForEach(t => t.IsPass = true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 위치 변경 이벤트 발생
|
// 위치 변경 이벤트 발생
|
||||||
PositionChanged?.Invoke(this, (_currentPosition, _currentDirection, _currentNode));
|
PositionChanged?.Invoke(this, (_currentPosition, _currentDirection, _currentNode));
|
||||||
}
|
}
|
||||||
@@ -408,6 +646,89 @@ namespace AGVNavigationCore.Models
|
|||||||
|
|
||||||
#region Private Methods
|
#region Private Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DetailedPath에서 노드 정보를 찾아 AGVCommand 생성
|
||||||
|
/// </summary>
|
||||||
|
private AGVCommand GetCommandFromPath(string targetNodeId, string actionDescription)
|
||||||
|
{
|
||||||
|
// DetailedPath가 없으면 기본 명령 반환
|
||||||
|
if (_currentPath == null || _currentPath.DetailedPath == null || _currentPath.DetailedPath.Count == 0)
|
||||||
|
{
|
||||||
|
var defaultMotor = _currentDirection == AgvDirection.Forward
|
||||||
|
? MotorCommand.Forward
|
||||||
|
: MotorCommand.Backward;
|
||||||
|
|
||||||
|
return new AGVCommand(
|
||||||
|
defaultMotor,
|
||||||
|
MagnetPosition.S,
|
||||||
|
SpeedLevel.M,
|
||||||
|
$"{actionDescription} (DetailedPath 없음)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetailedPath에서 targetNodeId에 해당하는 NodeMotorInfo 찾기
|
||||||
|
// 지나가지 않은 경로를 찾는다
|
||||||
|
var nodeInfo = _currentPath.DetailedPath.FirstOrDefault(n => n.NodeId == targetNodeId && n.IsPass == false);
|
||||||
|
|
||||||
|
if (nodeInfo == null)
|
||||||
|
{
|
||||||
|
// 못 찾으면 기본 명령 반환
|
||||||
|
var defaultMotor = _currentDirection == AgvDirection.Forward
|
||||||
|
? MotorCommand.Forward
|
||||||
|
: MotorCommand.Backward;
|
||||||
|
|
||||||
|
return new AGVCommand(
|
||||||
|
defaultMotor,
|
||||||
|
MagnetPosition.S,
|
||||||
|
SpeedLevel.M,
|
||||||
|
$"{actionDescription} (노드 {targetNodeId} 정보 없음)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MotorDirection → MotorCommand 변환
|
||||||
|
MotorCommand motorCmd;
|
||||||
|
switch (nodeInfo.MotorDirection)
|
||||||
|
{
|
||||||
|
case AgvDirection.Forward:
|
||||||
|
motorCmd = MotorCommand.Forward;
|
||||||
|
break;
|
||||||
|
case AgvDirection.Backward:
|
||||||
|
motorCmd = MotorCommand.Backward;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
motorCmd = MotorCommand.Stop;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MagnetDirection → MagnetPosition 변换
|
||||||
|
MagnetPosition magnetPos;
|
||||||
|
switch (nodeInfo.MagnetDirection)
|
||||||
|
{
|
||||||
|
case PathFinding.Planning.MagnetDirection.Left:
|
||||||
|
magnetPos = MagnetPosition.L;
|
||||||
|
break;
|
||||||
|
case PathFinding.Planning.MagnetDirection.Right:
|
||||||
|
magnetPos = MagnetPosition.R;
|
||||||
|
break;
|
||||||
|
case PathFinding.Planning.MagnetDirection.Straight:
|
||||||
|
default:
|
||||||
|
magnetPos = MagnetPosition.S;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 속도 결정 (회전 노드면 저속, 일반 이동은 중속)
|
||||||
|
SpeedLevel speed = nodeInfo.CanRotate || nodeInfo.IsDirectionChangePoint
|
||||||
|
? SpeedLevel.L
|
||||||
|
: SpeedLevel.M;
|
||||||
|
|
||||||
|
return new AGVCommand(
|
||||||
|
motorCmd,
|
||||||
|
magnetPos,
|
||||||
|
speed,
|
||||||
|
$"{actionDescription} → {targetNodeId} (Motor:{motorCmd}, Magnet:{magnetPos})"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private void StartMovement()
|
private void StartMovement()
|
||||||
{
|
{
|
||||||
SetState(AGVState.Moving);
|
SetState(AGVState.Moving);
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AGVPathResult FindPath_test(MapNode startNode, MapNode targetNode,
|
public AGVPathResult FindPath(MapNode startNode, MapNode targetNode,
|
||||||
MapNode prevNode, AgvDirection prevDirection, AgvDirection currentDirection, bool crossignore = false)
|
MapNode prevNode, AgvDirection prevDirection, AgvDirection currentDirection, bool crossignore = false)
|
||||||
{
|
{
|
||||||
// 입력 검증
|
// 입력 검증
|
||||||
@@ -154,6 +154,8 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
{
|
{
|
||||||
MakeDetailData(pathResult, currentDirection);
|
MakeDetailData(pathResult, currentDirection);
|
||||||
MakeMagnetDirection(pathResult);
|
MakeMagnetDirection(pathResult);
|
||||||
|
for (int i = 0; i < pathResult.DetailedPath.Count; i++)
|
||||||
|
pathResult.DetailedPath[i].seq = i + 1;
|
||||||
return pathResult;
|
return pathResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,6 +184,8 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
{
|
{
|
||||||
MakeDetailData(pathResult, ReverseDirection);
|
MakeDetailData(pathResult, ReverseDirection);
|
||||||
MakeMagnetDirection(pathResult);
|
MakeMagnetDirection(pathResult);
|
||||||
|
for (int i = 0; i < pathResult.DetailedPath.Count; i++)
|
||||||
|
pathResult.DetailedPath[i].seq = i + 1;
|
||||||
return pathResult;
|
return pathResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,6 +200,8 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
{
|
{
|
||||||
MakeDetailData(pathResult, currentDirection);
|
MakeDetailData(pathResult, currentDirection);
|
||||||
MakeMagnetDirection(pathResult);
|
MakeMagnetDirection(pathResult);
|
||||||
|
for (int i = 0; i < pathResult.DetailedPath.Count; i++)
|
||||||
|
pathResult.DetailedPath[i].seq = i + 1;
|
||||||
return pathResult;
|
return pathResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +226,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
Path0.PrevDirection = prevDirection;
|
Path0.PrevDirection = prevDirection;
|
||||||
MakeDetailData(Path0, prevDirection);
|
MakeDetailData(Path0, prevDirection);
|
||||||
|
|
||||||
var Path1 = FindPath_test(nextNodeForward, targetNode, startNode, prevDirection, currentDirection, true);
|
var Path1 = FindPath(nextNodeForward, targetNode, startNode, prevDirection, currentDirection, true);
|
||||||
Path1.PrevNode = startNode;
|
Path1.PrevNode = startNode;
|
||||||
Path1.PrevDirection = prevDirection;
|
Path1.PrevDirection = prevDirection;
|
||||||
//MakeDetailData(Path1, ReverseDirection);
|
//MakeDetailData(Path1, ReverseDirection);
|
||||||
@@ -228,6 +234,9 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
var combinedResult0 = Path0;
|
var combinedResult0 = Path0;
|
||||||
combinedResult0 = _basicPathfinder.CombineResults(combinedResult0, Path1);
|
combinedResult0 = _basicPathfinder.CombineResults(combinedResult0, Path1);
|
||||||
MakeMagnetDirection(combinedResult0);
|
MakeMagnetDirection(combinedResult0);
|
||||||
|
|
||||||
|
for (int i = 0; i < combinedResult0.DetailedPath.Count; i++)
|
||||||
|
combinedResult0.DetailedPath[i].seq = i + 1;
|
||||||
return combinedResult0;
|
return combinedResult0;
|
||||||
}
|
}
|
||||||
else if (nextNodeBackward != null)
|
else if (nextNodeBackward != null)
|
||||||
@@ -404,6 +413,8 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
|
|
||||||
MakeMagnetDirection(combinedResult);
|
MakeMagnetDirection(combinedResult);
|
||||||
|
|
||||||
|
for (int i = 0; i < combinedResult.DetailedPath.Count; i++)
|
||||||
|
combinedResult.DetailedPath[i].seq = i + 1;
|
||||||
return combinedResult;
|
return combinedResult;
|
||||||
|
|
||||||
|
|
||||||
@@ -427,7 +438,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
string nextNodeId = (i + 1 < path1.Path.Count) ? path1.Path[i + 1].NodeId : null;
|
string nextNodeId = (i + 1 < path1.Path.Count) ? path1.Path[i + 1].NodeId : null;
|
||||||
|
|
||||||
// 노드 정보 생성 (현재 방향 유지)
|
// 노드 정보 생성 (현재 방향 유지)
|
||||||
var nodeInfo = new NodeMotorInfo(
|
var nodeInfo = new NodeMotorInfo(i+1,
|
||||||
nodeId, RfidId,
|
nodeId, RfidId,
|
||||||
currentDirection,
|
currentDirection,
|
||||||
nextNodeId,
|
nextNodeId,
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class NodeMotorInfo
|
public class NodeMotorInfo
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 일련번호
|
||||||
|
/// </summary>
|
||||||
|
public int seq { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 노드 ID
|
/// 노드 ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -68,13 +72,19 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RequiresSpecialAction { get; set; }
|
public bool RequiresSpecialAction { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 해당노드가 인식되면 이 값이 셋팅됩니다.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPass { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 특수 동작 설명
|
/// 특수 동작 설명
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SpecialActionDescription { get; set; }
|
public string SpecialActionDescription { get; set; }
|
||||||
|
|
||||||
public NodeMotorInfo(string nodeId,string rfid, AgvDirection motorDirection, string nextNodeId = null, MagnetDirection magnetDirection = MagnetDirection.Straight)
|
public NodeMotorInfo(int seqno,string nodeId,string rfid, AgvDirection motorDirection, string nextNodeId = null, MagnetDirection magnetDirection = MagnetDirection.Straight)
|
||||||
{
|
{
|
||||||
|
seq = seqno;
|
||||||
NodeId = nodeId;
|
NodeId = nodeId;
|
||||||
RfidId = rfid;
|
RfidId = rfid;
|
||||||
MotorDirection = motorDirection;
|
MotorDirection = motorDirection;
|
||||||
@@ -84,6 +94,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
IsDirectionChangePoint = false;
|
IsDirectionChangePoint = false;
|
||||||
RequiresSpecialAction = false;
|
RequiresSpecialAction = false;
|
||||||
SpecialActionDescription = string.Empty;
|
SpecialActionDescription = string.Empty;
|
||||||
|
IsPass = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ namespace AGVSimulator.Forms
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
|
this.components = new System.ComponentModel.Container();
|
||||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SimulatorForm));
|
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SimulatorForm));
|
||||||
this._menuStrip = new System.Windows.Forms.MenuStrip();
|
this._menuStrip = new System.Windows.Forms.MenuStrip();
|
||||||
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
@@ -77,6 +78,7 @@ namespace AGVSimulator.Forms
|
|||||||
this.resetZoomToolStripButton = new System.Windows.Forms.ToolStripButton();
|
this.resetZoomToolStripButton = new System.Windows.Forms.ToolStripButton();
|
||||||
this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator();
|
this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
this.toolStripButton1 = new System.Windows.Forms.ToolStripButton();
|
this.toolStripButton1 = new System.Windows.Forms.ToolStripButton();
|
||||||
|
this.btPredict = new System.Windows.Forms.ToolStripButton();
|
||||||
this._statusStrip = new System.Windows.Forms.StatusStrip();
|
this._statusStrip = new System.Windows.Forms.StatusStrip();
|
||||||
this._statusLabel = new System.Windows.Forms.ToolStripStatusLabel();
|
this._statusLabel = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
this._coordLabel = new System.Windows.Forms.ToolStripStatusLabel();
|
this._coordLabel = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
@@ -112,6 +114,8 @@ namespace AGVSimulator.Forms
|
|||||||
this._agvInfoTitleLabel = new System.Windows.Forms.Label();
|
this._agvInfoTitleLabel = new System.Windows.Forms.Label();
|
||||||
this._liftDirectionLabel = new System.Windows.Forms.Label();
|
this._liftDirectionLabel = new System.Windows.Forms.Label();
|
||||||
this._motorDirectionLabel = new System.Windows.Forms.Label();
|
this._motorDirectionLabel = new System.Windows.Forms.Label();
|
||||||
|
this.lbPredict = new System.Windows.Forms.RichTextBox();
|
||||||
|
this.timer1 = new System.Windows.Forms.Timer(this.components);
|
||||||
this._menuStrip.SuspendLayout();
|
this._menuStrip.SuspendLayout();
|
||||||
this._toolStrip.SuspendLayout();
|
this._toolStrip.SuspendLayout();
|
||||||
this._statusStrip.SuspendLayout();
|
this._statusStrip.SuspendLayout();
|
||||||
@@ -119,6 +123,7 @@ namespace AGVSimulator.Forms
|
|||||||
this._statusGroup.SuspendLayout();
|
this._statusGroup.SuspendLayout();
|
||||||
this._pathGroup.SuspendLayout();
|
this._pathGroup.SuspendLayout();
|
||||||
this._agvControlGroup.SuspendLayout();
|
this._agvControlGroup.SuspendLayout();
|
||||||
|
this._canvasPanel.SuspendLayout();
|
||||||
this._agvInfoPanel.SuspendLayout();
|
this._agvInfoPanel.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
@@ -131,7 +136,7 @@ namespace AGVSimulator.Forms
|
|||||||
this.helpToolStripMenuItem});
|
this.helpToolStripMenuItem});
|
||||||
this._menuStrip.Location = new System.Drawing.Point(0, 0);
|
this._menuStrip.Location = new System.Drawing.Point(0, 0);
|
||||||
this._menuStrip.Name = "_menuStrip";
|
this._menuStrip.Name = "_menuStrip";
|
||||||
this._menuStrip.Size = new System.Drawing.Size(1200, 24);
|
this._menuStrip.Size = new System.Drawing.Size(1034, 24);
|
||||||
this._menuStrip.TabIndex = 0;
|
this._menuStrip.TabIndex = 0;
|
||||||
this._menuStrip.Text = "menuStrip";
|
this._menuStrip.Text = "menuStrip";
|
||||||
//
|
//
|
||||||
@@ -279,10 +284,11 @@ namespace AGVSimulator.Forms
|
|||||||
this.fitToMapToolStripButton,
|
this.fitToMapToolStripButton,
|
||||||
this.resetZoomToolStripButton,
|
this.resetZoomToolStripButton,
|
||||||
this.toolStripSeparator5,
|
this.toolStripSeparator5,
|
||||||
this.toolStripButton1});
|
this.toolStripButton1,
|
||||||
|
this.btPredict});
|
||||||
this._toolStrip.Location = new System.Drawing.Point(0, 24);
|
this._toolStrip.Location = new System.Drawing.Point(0, 24);
|
||||||
this._toolStrip.Name = "_toolStrip";
|
this._toolStrip.Name = "_toolStrip";
|
||||||
this._toolStrip.Size = new System.Drawing.Size(1200, 25);
|
this._toolStrip.Size = new System.Drawing.Size(1034, 25);
|
||||||
this._toolStrip.TabIndex = 1;
|
this._toolStrip.TabIndex = 1;
|
||||||
this._toolStrip.Text = "toolStrip";
|
this._toolStrip.Text = "toolStrip";
|
||||||
//
|
//
|
||||||
@@ -387,19 +393,28 @@ namespace AGVSimulator.Forms
|
|||||||
this.toolStripButton1.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton1.Image")));
|
this.toolStripButton1.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton1.Image")));
|
||||||
this.toolStripButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
|
this.toolStripButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||||
this.toolStripButton1.Name = "toolStripButton1";
|
this.toolStripButton1.Name = "toolStripButton1";
|
||||||
this.toolStripButton1.Size = new System.Drawing.Size(75, 22);
|
this.toolStripButton1.Size = new System.Drawing.Size(111, 22);
|
||||||
this.toolStripButton1.Text = "경로예측";
|
this.toolStripButton1.Text = "전체경로테스트";
|
||||||
this.toolStripButton1.Click += new System.EventHandler(this.toolStripButton1_Click);
|
this.toolStripButton1.Click += new System.EventHandler(this.toolStripButton1_Click);
|
||||||
//
|
//
|
||||||
|
// btPredict
|
||||||
|
//
|
||||||
|
this.btPredict.Image = ((System.Drawing.Image)(resources.GetObject("btPredict.Image")));
|
||||||
|
this.btPredict.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||||
|
this.btPredict.Name = "btPredict";
|
||||||
|
this.btPredict.Size = new System.Drawing.Size(107, 22);
|
||||||
|
this.btPredict.Text = "다음 행동 예측";
|
||||||
|
this.btPredict.Click += new System.EventHandler(this.btPredict_Click);
|
||||||
|
//
|
||||||
// _statusStrip
|
// _statusStrip
|
||||||
//
|
//
|
||||||
this._statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this._statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
this._statusLabel,
|
this._statusLabel,
|
||||||
this._coordLabel,
|
this._coordLabel,
|
||||||
this.prb1});
|
this.prb1});
|
||||||
this._statusStrip.Location = new System.Drawing.Point(0, 778);
|
this._statusStrip.Location = new System.Drawing.Point(0, 689);
|
||||||
this._statusStrip.Name = "_statusStrip";
|
this._statusStrip.Name = "_statusStrip";
|
||||||
this._statusStrip.Size = new System.Drawing.Size(1200, 22);
|
this._statusStrip.Size = new System.Drawing.Size(1034, 22);
|
||||||
this._statusStrip.TabIndex = 2;
|
this._statusStrip.TabIndex = 2;
|
||||||
this._statusStrip.Text = "statusStrip";
|
this._statusStrip.Text = "statusStrip";
|
||||||
//
|
//
|
||||||
@@ -426,9 +441,9 @@ namespace AGVSimulator.Forms
|
|||||||
this._controlPanel.Controls.Add(this._pathGroup);
|
this._controlPanel.Controls.Add(this._pathGroup);
|
||||||
this._controlPanel.Controls.Add(this._agvControlGroup);
|
this._controlPanel.Controls.Add(this._agvControlGroup);
|
||||||
this._controlPanel.Dock = System.Windows.Forms.DockStyle.Right;
|
this._controlPanel.Dock = System.Windows.Forms.DockStyle.Right;
|
||||||
this._controlPanel.Location = new System.Drawing.Point(967, 49);
|
this._controlPanel.Location = new System.Drawing.Point(801, 49);
|
||||||
this._controlPanel.Name = "_controlPanel";
|
this._controlPanel.Name = "_controlPanel";
|
||||||
this._controlPanel.Size = new System.Drawing.Size(233, 729);
|
this._controlPanel.Size = new System.Drawing.Size(233, 640);
|
||||||
this._controlPanel.TabIndex = 3;
|
this._controlPanel.TabIndex = 3;
|
||||||
//
|
//
|
||||||
// _statusGroup
|
// _statusGroup
|
||||||
@@ -679,10 +694,11 @@ namespace AGVSimulator.Forms
|
|||||||
//
|
//
|
||||||
// _canvasPanel
|
// _canvasPanel
|
||||||
//
|
//
|
||||||
|
this._canvasPanel.Controls.Add(this.lbPredict);
|
||||||
this._canvasPanel.Dock = System.Windows.Forms.DockStyle.Fill;
|
this._canvasPanel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this._canvasPanel.Location = new System.Drawing.Point(0, 129);
|
this._canvasPanel.Location = new System.Drawing.Point(0, 129);
|
||||||
this._canvasPanel.Name = "_canvasPanel";
|
this._canvasPanel.Name = "_canvasPanel";
|
||||||
this._canvasPanel.Size = new System.Drawing.Size(967, 649);
|
this._canvasPanel.Size = new System.Drawing.Size(801, 560);
|
||||||
this._canvasPanel.TabIndex = 4;
|
this._canvasPanel.TabIndex = 4;
|
||||||
//
|
//
|
||||||
// _agvInfoPanel
|
// _agvInfoPanel
|
||||||
@@ -696,7 +712,7 @@ namespace AGVSimulator.Forms
|
|||||||
this._agvInfoPanel.Dock = System.Windows.Forms.DockStyle.Top;
|
this._agvInfoPanel.Dock = System.Windows.Forms.DockStyle.Top;
|
||||||
this._agvInfoPanel.Location = new System.Drawing.Point(0, 49);
|
this._agvInfoPanel.Location = new System.Drawing.Point(0, 49);
|
||||||
this._agvInfoPanel.Name = "_agvInfoPanel";
|
this._agvInfoPanel.Name = "_agvInfoPanel";
|
||||||
this._agvInfoPanel.Size = new System.Drawing.Size(967, 80);
|
this._agvInfoPanel.Size = new System.Drawing.Size(801, 80);
|
||||||
this._agvInfoPanel.TabIndex = 5;
|
this._agvInfoPanel.TabIndex = 5;
|
||||||
//
|
//
|
||||||
// _pathDebugLabel
|
// _pathDebugLabel
|
||||||
@@ -741,11 +757,25 @@ namespace AGVSimulator.Forms
|
|||||||
this._motorDirectionLabel.TabIndex = 2;
|
this._motorDirectionLabel.TabIndex = 2;
|
||||||
this._motorDirectionLabel.Text = "모터 방향: -";
|
this._motorDirectionLabel.Text = "모터 방향: -";
|
||||||
//
|
//
|
||||||
|
// lbPredict
|
||||||
|
//
|
||||||
|
this.lbPredict.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
|
this.lbPredict.Location = new System.Drawing.Point(0, 513);
|
||||||
|
this.lbPredict.Name = "lbPredict";
|
||||||
|
this.lbPredict.Size = new System.Drawing.Size(801, 47);
|
||||||
|
this.lbPredict.TabIndex = 0;
|
||||||
|
this.lbPredict.Text = "";
|
||||||
|
//
|
||||||
|
// timer1
|
||||||
|
//
|
||||||
|
this.timer1.Interval = 500;
|
||||||
|
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
|
||||||
|
//
|
||||||
// SimulatorForm
|
// SimulatorForm
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(1200, 800);
|
this.ClientSize = new System.Drawing.Size(1034, 711);
|
||||||
this.Controls.Add(this._canvasPanel);
|
this.Controls.Add(this._canvasPanel);
|
||||||
this.Controls.Add(this._agvInfoPanel);
|
this.Controls.Add(this._agvInfoPanel);
|
||||||
this.Controls.Add(this._controlPanel);
|
this.Controls.Add(this._controlPanel);
|
||||||
@@ -770,6 +800,7 @@ namespace AGVSimulator.Forms
|
|||||||
this._pathGroup.PerformLayout();
|
this._pathGroup.PerformLayout();
|
||||||
this._agvControlGroup.ResumeLayout(false);
|
this._agvControlGroup.ResumeLayout(false);
|
||||||
this._agvControlGroup.PerformLayout();
|
this._agvControlGroup.PerformLayout();
|
||||||
|
this._canvasPanel.ResumeLayout(false);
|
||||||
this._agvInfoPanel.ResumeLayout(false);
|
this._agvInfoPanel.ResumeLayout(false);
|
||||||
this._agvInfoPanel.PerformLayout();
|
this._agvInfoPanel.PerformLayout();
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
@@ -845,5 +876,8 @@ namespace AGVSimulator.Forms
|
|||||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator5;
|
private System.Windows.Forms.ToolStripSeparator toolStripSeparator5;
|
||||||
private System.Windows.Forms.ToolStripButton toolStripButton1;
|
private System.Windows.Forms.ToolStripButton toolStripButton1;
|
||||||
private System.Windows.Forms.ToolStripProgressBar prb1;
|
private System.Windows.Forms.ToolStripProgressBar prb1;
|
||||||
|
private System.Windows.Forms.ToolStripButton btPredict;
|
||||||
|
private System.Windows.Forms.RichTextBox lbPredict;
|
||||||
|
private System.Windows.Forms.Timer timer1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,6 +174,8 @@ namespace AGVSimulator.Forms
|
|||||||
_statusLabel.Text = "시뮬레이션 실행 중";
|
_statusLabel.Text = "시뮬레이션 실행 중";
|
||||||
Console.WriteLine("시뮬레이션 실행");
|
Console.WriteLine("시뮬레이션 실행");
|
||||||
UpdateUI();
|
UpdateUI();
|
||||||
|
|
||||||
|
timer1.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStopSimulation_Click(object sender, EventArgs e)
|
private void OnStopSimulation_Click(object sender, EventArgs e)
|
||||||
@@ -317,7 +319,7 @@ namespace AGVSimulator.Forms
|
|||||||
var prevDir = selectedAGV.PrevDirection;
|
var prevDir = selectedAGV.PrevDirection;
|
||||||
|
|
||||||
// 고급 경로 계획 사용 (노드 객체 직접 전달)
|
// 고급 경로 계획 사용 (노드 객체 직접 전달)
|
||||||
var advancedResult = _advancedPathfinder.FindPath_test(startNode, targetNode, prevNode, prevDir, currentDirection);
|
var advancedResult = _advancedPathfinder.FindPath(startNode, targetNode, prevNode, prevDir, currentDirection);
|
||||||
|
|
||||||
_simulatorCanvas.FitToNodes();
|
_simulatorCanvas.FitToNodes();
|
||||||
if (advancedResult.Success)
|
if (advancedResult.Success)
|
||||||
@@ -325,10 +327,6 @@ namespace AGVSimulator.Forms
|
|||||||
// 도킹 검증이 없는 경우 추가 검증 수행
|
// 도킹 검증이 없는 경우 추가 검증 수행
|
||||||
if (advancedResult.DockingValidation == null || !advancedResult.DockingValidation.IsValidationRequired)
|
if (advancedResult.DockingValidation == null || !advancedResult.DockingValidation.IsValidationRequired)
|
||||||
{
|
{
|
||||||
if (advancedResult.Path.Count < 1)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _mapNodes);
|
advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _mapNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,6 +334,9 @@ namespace AGVSimulator.Forms
|
|||||||
_pathLengthLabel.Text = $"경로 길이: {advancedResult.TotalDistance:F1}";
|
_pathLengthLabel.Text = $"경로 길이: {advancedResult.TotalDistance:F1}";
|
||||||
_statusLabel.Text = $"경로 계산 완료 ({advancedResult.CalculationTimeMs}ms)";
|
_statusLabel.Text = $"경로 계산 완료 ({advancedResult.CalculationTimeMs}ms)";
|
||||||
|
|
||||||
|
// 🔥 VirtualAGV에도 경로 설정 (Predict()가 동작하려면 필요)
|
||||||
|
selectedAGV.SetPath(advancedResult);
|
||||||
|
|
||||||
// 도킹 검증 결과 확인 및 UI 표시
|
// 도킹 검증 결과 확인 및 UI 표시
|
||||||
CheckAndDisplayDockingValidation(advancedResult);
|
CheckAndDisplayDockingValidation(advancedResult);
|
||||||
|
|
||||||
@@ -357,6 +358,12 @@ namespace AGVSimulator.Forms
|
|||||||
_simulatorCanvas.CurrentPath = null;
|
_simulatorCanvas.CurrentPath = null;
|
||||||
_pathLengthLabel.Text = "경로 길이: -";
|
_pathLengthLabel.Text = "경로 길이: -";
|
||||||
_statusLabel.Text = "경로 지움";
|
_statusLabel.Text = "경로 지움";
|
||||||
|
|
||||||
|
// 🔥 VirtualAGV의 경로도 정지
|
||||||
|
if (_agvList != null && _agvList.Count > 0)
|
||||||
|
{
|
||||||
|
_agvList[0].StopPath();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTargetCalc_Click(object sender, EventArgs e)
|
private void OnTargetCalc_Click(object sender, EventArgs e)
|
||||||
@@ -590,7 +597,6 @@ namespace AGVSimulator.Forms
|
|||||||
|
|
||||||
// AGV 위치 및 방향 설정
|
// AGV 위치 및 방향 설정
|
||||||
_simulatorCanvas.SetAGVPosition(selectedAGV.AgvId, targetNode, selectedDirection);
|
_simulatorCanvas.SetAGVPosition(selectedAGV.AgvId, targetNode, selectedDirection);
|
||||||
_simulatorCanvas.UpdateAGVDirection(selectedAGV.AgvId, selectedDirection);
|
|
||||||
|
|
||||||
// VirtualAGV 객체의 위치와 방향 업데이트
|
// VirtualAGV 객체의 위치와 방향 업데이트
|
||||||
selectedAGV.SetPosition(targetNode, selectedDirection); // 이전 위치 기억하도록
|
selectedAGV.SetPosition(targetNode, selectedDirection); // 이전 위치 기억하도록
|
||||||
@@ -1576,6 +1582,49 @@ namespace AGVSimulator.Forms
|
|||||||
logForm.AppendLog($"성공률: {(double)successCount / totalTests * 100:F1}%");
|
logForm.AppendLog($"성공률: {(double)successCount / totalTests * 100:F1}%");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void btPredict_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// 다음 행동 예측
|
||||||
|
if (_agvList == null || _agvList.Count == 0)
|
||||||
|
{
|
||||||
|
MessageBox.Show("AGV가 없습니다.", "예측 오류", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 첫 번째 AGV의 다음 행동 예측
|
||||||
|
var agv = _agvList[0];
|
||||||
|
var command = agv.Predict();
|
||||||
|
|
||||||
|
//this.lbPredict.Text = $"MOT:{command.Motor},MAG:{command.Magnet},SPD:{command.Speed}:{command.Reason}";
|
||||||
|
|
||||||
|
// 예측 결과 표시
|
||||||
|
var message = $"[다음 행동 예측]\n\n" +
|
||||||
|
$"모터: {command.Motor}\n" +
|
||||||
|
$"마그넷: {command.Magnet}\n" +
|
||||||
|
$"속도: {command.Speed}\n" +
|
||||||
|
$"이유: {command.Reason}\n\n" +
|
||||||
|
$"---\n" +
|
||||||
|
$"현재 상태: {agv.CurrentState}\n" +
|
||||||
|
$"현재 방향: {agv.CurrentDirection}\n" +
|
||||||
|
$"위치 확정: {agv.IsPositionConfirmed} (RFID {agv.DetectedRfidCount}개)\n" +
|
||||||
|
$"현재 노드: {agv.CurrentNodeId ?? "없음"}";
|
||||||
|
|
||||||
|
Console.WriteLine(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void timer1_Tick(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (_agvList == null || _agvList.Count == 0)
|
||||||
|
{
|
||||||
|
// MessageBox.Show("AGV가 없습니다.", "예측 오류", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 첫 번째 AGV의 다음 행동 예측
|
||||||
|
var agv = _agvList[0];
|
||||||
|
var command = agv.Predict();
|
||||||
|
this.lbPredict.Text = $"Motor:{command.Motor},Magnet:{command.Magnet},Speed:{command.Speed} : {command.Reason}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -137,9 +137,27 @@
|
|||||||
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="btPredict.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
|
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||||
|
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||||
|
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||||
|
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||||
|
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||||
|
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||||
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<metadata name="_statusStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
<metadata name="_statusStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
<value>237, 17</value>
|
<value>237, 17</value>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
<metadata name="timer1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>352, 17</value>
|
||||||
|
</metadata>
|
||||||
</root>
|
</root>
|
||||||
Reference in New Issue
Block a user