<fix>위치 정보 수정/삭제 완료

This commit is contained in:
SOOBEEN HAN
2025-11-07 16:26:43 +09:00
parent 912294cbe4
commit 97d4916ff0
6 changed files with 186 additions and 42 deletions

View File

@@ -48,6 +48,7 @@ public class BoxController : MonoBehaviour
// 로봇 팔을 부모로 설정하여 따라다니게 함 // 로봇 팔을 부모로 설정하여 따라다니게 함
attachParent = other.transform; attachParent = other.transform;
transform.SetParent(attachParent); transform.SetParent(attachParent);
transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
// 부착된 동안 물리적 충돌이나 중력을 무시하도록 Rigidbody를 Kinematic으로 설정 // 부착된 동안 물리적 충돌이나 중력을 무시하도록 Rigidbody를 Kinematic으로 설정
if (rb != null) if (rb != null)

View File

@@ -524,6 +524,9 @@ public class ProgramModel : IProgramModel
public async Task<string> GetMovingState() public async Task<string> GetMovingState()
{ {
return await tcpClient.SendGetRequestAsync("/project/robot/moving_to_pose_manual"); string jsonResponse = await tcpClient.SendGetRequestAsync("/project/robot/moving_to_pose_manual");
string parsingJsonResponse = HttpResponseParser.ExtractJsonFromHttpResponse(jsonResponse);
return parsingJsonResponse;
} }
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading; using System.Threading;
@@ -76,6 +77,9 @@ public class ProgramPresenter
this.controlledRobot = robot; this.controlledRobot = robot;
this.controlledRobot.OnPoseUpdateRequest += HandleGETPose; this.controlledRobot.OnPoseUpdateRequest += HandleGETPose;
popPos = controlledRobot.GetEndPoint(); popPos = controlledRobot.GetEndPoint();
if (controlledRobot != null)
controlledRobot.StartCoroutine(UpdateRobotMovingStateCoroutine());
} }
public async Task UpdateMotorStateAsync() public async Task UpdateMotorStateAsync()
@@ -96,6 +100,37 @@ public class ProgramPresenter
} }
} }
private IEnumerator UpdateRobotMovingStateCoroutine()
{
var waitForOneSecond = new WaitForSeconds(1.0f);
while (true)
{
Task<string> getTask = model.GetMovingState();
yield return new WaitUntil(() => getTask.IsCompleted);
try
{
if (getTask.IsFaulted)
{
throw getTask.Exception.InnerException;
}
string currentState = getTask.Result;
if (pointManagerView.movingAlert != null)
{
pointManagerView.movingAlert.SetActive(currentState == "1");
}
}
catch (Exception e)
{
Debug.LogWarning($"로봇 moving 상태 업데이트 실패: {e.Message}");
}
yield return waitForOneSecond;
}
}
public void OnApplicationStart() public void OnApplicationStart()
{ {
if (controlledRobot != null) if (controlledRobot != null)
@@ -220,7 +255,7 @@ public class ProgramPresenter
this.latestTargetPose = ConvertPoseToRobotData(newWorldPos, newWorldRot); this.latestTargetPose = ConvertPoseToRobotData(newWorldPos, newWorldRot);
await model.MoveToPoseTcpAsync(newWorldPos); await model.MoveToPoseTcpAsync(newWorldPos);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -232,9 +267,10 @@ public class ProgramPresenter
private void HandleRobotReleased(RobotData pose) private void HandleRobotReleased(RobotData pose)
{ {
IsDragging = false; IsDragging = false;
pendingPointData = pose; // 임시 저장 pendingPointData = this.latestTargetPose; // 임시 저장
currentPopupState = PopupState.ConfirmAddPoint; // 상태 설정 currentPopupState = PopupState.ConfirmAddPoint; // 상태 설정
popupView.ShowConfirmPopup(popPos); // 팝업 요청
_ = WaitForRobotToStopAndShowPopup(currentPopupState);
} }
private void StopDragMoveLoop() private void StopDragMoveLoop()
@@ -245,11 +281,11 @@ public class ProgramPresenter
} }
// --- 포인트 클릭 --- // --- 포인트 클릭 ---
private void HandlePointClicked(int index) private void HandlePointClicked(int index, Vector3 newWorldPos)
{ {
activePointIndex = index; // 인덱스 저장 activePointIndex = index; // 인덱스 저장
currentPopupState = PopupState.MoveOrDelete; // 상태 설정 currentPopupState = PopupState.MoveOrDelete; // 상태 설정
popupView.ShowOptionPopup(popPos); // 팝업 요청 popupView.ShowOptionPopup(newWorldPos); // 팝업 요청
} }
// --- 포인트 드래그 --- // --- 포인트 드래그 ---
@@ -283,7 +319,7 @@ public class ProgramPresenter
RedrawTemporaryPath(index, latestTargetPose); RedrawTemporaryPath(index, latestTargetPose);
} }
private void HandlePointDragEnd(int index) private async void HandlePointDragEnd(int index)
{ {
IsDragging = false; IsDragging = false;
@@ -291,10 +327,14 @@ public class ProgramPresenter
interactionView.HideGhostRobot(); interactionView.HideGhostRobot();
pendingPointData = this.latestTargetPose; pendingPointData = this.latestTargetPose;
currentPopupState = PopupState.ConfirmModifyPoint; // 상태 설정
popupView.ShowConfirmPopup(popPos);
}
Vector3 popupWorldPos = ConvertRobotDataToVector3(pendingPointData);
await model.MoveToPoseTcpAsync(popupWorldPos);
currentPopupState = PopupState.ConfirmModifyPoint; // 상태 설정
popupView.ShowModifyPopup(popPos);
}
// --- 팝업 응답 통합 핸들러 --- // --- 팝업 응답 통합 핸들러 ---
private async void HandlePopupResponse(PopupResponse response) private async void HandlePopupResponse(PopupResponse response)
@@ -307,7 +347,7 @@ public class ProgramPresenter
case PopupState.ConfirmAddPoint: case PopupState.ConfirmAddPoint:
if (response == PopupResponse.InsConfirm) if (response == PopupResponse.InsConfirm)
{ {
await model.SavePointToProgramAsync(pendingPointData); await model.SavePointToProgramAsync(pendingPointData, -1);
RedrawSceneFromModel(); // 뷰 갱신 RedrawSceneFromModel(); // 뷰 갱신
} }
break; break;
@@ -323,27 +363,23 @@ public class ProgramPresenter
else if (response == PopupResponse.Cancel) else if (response == PopupResponse.Cancel)
{ {
pointManagerView.UpdatePointPosition(activePointIndex, originalDragPose); // 원위치 pointManagerView.UpdatePointPosition(activePointIndex, originalDragPose); // 원위치
RedrawSceneFromModel(); // 경로 원위치 RedrawTemporaryPath(activePointIndex, originalDragPose); // 경로 원위치
} }
break; break;
// 여기로 이동/삭제 // 여기로 이동/삭제
case PopupState.MoveOrDelete: case PopupState.MoveOrDelete:
RobotData targetPose = model.CurrentProgram.GetStepPose(activePointIndex);
Vector3 pos = ConvertRobotDataToVector3(targetPose);
if (response == PopupResponse.Move) if (response == PopupResponse.Move)
{ {
RobotData targetPose = model.CurrentProgram.GetStepPose(activePointIndex);
Vector3 pos = ConvertRobotDataToVector3(targetPose);
// 알림 UI 표시
pointManagerView.movingAlert.SetActive(true);
await model.MoveToPoseTcpAsync(pos); await model.MoveToPoseTcpAsync(pos);
Debug.Log($"이동 중? : { await model.GetMovingState()}"); // TODO. 어떤 값을 받아오는지.
} }
else if (response == PopupResponse.Delete) else if (response == PopupResponse.Delete)
{ {
popupView.ShowDeletePopup(pos);
currentPopupState = PopupState.ConfirmDelete; currentPopupState = PopupState.ConfirmDelete;
popupView.ShowConfirmPopup(popPos); return;
} }
break; break;
@@ -362,10 +398,42 @@ public class ProgramPresenter
activePointIndex = -1; activePointIndex = -1;
} }
private async Task WaitForRobotToStopAndShowPopup(PopupState nextState)
{
while (await model.GetMovingState() == "1")
{
Debug.Log("로봇이 멈추기를 기다리는 중...");
await Task.Delay(100);
}
Debug.Log("로봇 정지 완료. 팝업 표시.");
currentPopupState = nextState;
Vector3 popupWorldPos = Vector3.zero;
if (nextState == PopupState.ConfirmAddPoint)
{
popupWorldPos = ConvertRobotDataToVector3(pendingPointData);
popupView.ShowConfirmPopupFromPoint(popupWorldPos); // (새 포인트 위치)
}
}
private async Task WaitForRobotToStop()
{
Debug.Log("로봇 이동 완료 대기 중...");
await Task.Delay(200); // (명령이 전달될 최소 시간)
while (await model.GetMovingState() == "1")
{
await Task.Delay(100);
}
Debug.Log("로봇 정지 완료.");
}
// Model의 현재 상태를 읽어 모든 View를 새로 고침 // Model의 현재 상태를 읽어 모든 View를 새로 고침
private void RedrawSceneFromModel() private async void RedrawSceneFromModel()
{ {
if (model.CurrentProgram == null) return; if (model.CurrentProgram == null) return;
await model.LoadProgram(model.CurrentProgram.ProgramId);
// RobotProgram.Steps (List<RobotMoveStep>)를 List<RobotData>로 변환 // RobotProgram.Steps (List<RobotMoveStep>)를 List<RobotData>로 변환
List<RobotData> poses = model.CurrentProgram.GetAllStepPoses(); List<RobotData> poses = model.CurrentProgram.GetAllStepPoses();

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
@@ -15,7 +16,7 @@ public class InteractionView : MonoBehaviour, IInteractionView
{ {
public event Action<Vector3, Quaternion> OnRobotGrabbed; public event Action<Vector3, Quaternion> OnRobotGrabbed;
public event Action<RobotData> OnRobotReleased; public event Action<RobotData> OnRobotReleased;
public event Action<int> OnPointClicked; public event Action<int, Vector3> OnPointClicked;
public event Action<int> OnPointDragStart; public event Action<int> OnPointDragStart;
public event Action<int, Vector3, Quaternion> OnPointDragUpdate; public event Action<int, Vector3, Quaternion> OnPointDragUpdate;
public event Action<int> OnPointDragEnd; public event Action<int> OnPointDragEnd;
@@ -30,11 +31,18 @@ public class InteractionView : MonoBehaviour, IInteractionView
[SerializeField] [SerializeField]
[Tooltip("드래그용 마우스 이미지")] [Tooltip("드래그용 마우스 이미지")]
private GameObject dragArrow; private GameObject dragArrow;
[SerializeField] private float clickTimeThreshold = 0.5f;
[SerializeField] private float dragMovementThreshold = 0.05f;
private Coroutine clickOrDragCoroutine = null;
private Vector3 startGrabPosition;
private bool isInitialized = false; private bool isInitialized = false;
private bool isGrabbingPoint = false; private bool isGrabbingPoint = false;
private int currentGrabbedPointIndex = -1; private int currentGrabbedPointIndex = -1;
Vector3 currentTargetPosition = Vector3.zero;
Quaternion currentTargetRotation = Quaternion.identity;
public bool isGrabbingRobot = false; public bool isGrabbingRobot = false;
void Start() void Start()
@@ -76,21 +84,29 @@ public class InteractionView : MonoBehaviour, IInteractionView
} }
else else
{ {
Vector3 currentTargetPosition = Vector3.zero;
Quaternion currentTargetRotation = Quaternion.identity;
currentTargetPosition = rayProvider.rayEndPoint; currentTargetPosition = rayProvider.rayEndPoint;
currentTargetRotation = baseInteractor.attachTransform.rotation; currentTargetRotation = baseInteractor.attachTransform.rotation;
if (isGrabbingRobot)
{
OnRobotGrabbed?.Invoke(currentTargetPosition, currentTargetRotation);
return;
}
if (isGrabbingPoint) if (isGrabbingPoint)
{ {
OnPointDragUpdate?.Invoke(currentGrabbedPointIndex, currentTargetPosition, currentTargetRotation); OnPointDragUpdate?.Invoke(currentGrabbedPointIndex, currentTargetPosition, currentTargetRotation);
} }
else if (isGrabbingRobot) else if (clickOrDragCoroutine != null)
{ {
OnRobotGrabbed?.Invoke(currentTargetPosition, currentTargetRotation); float distance = Vector3.Distance(startGrabPosition, currentTargetPosition);
if (distance > dragMovementThreshold)
{
StartDragMode();
}
} }
} }
} }
@@ -110,8 +126,6 @@ public class InteractionView : MonoBehaviour, IInteractionView
{ {
baseInteractor.selectEntered.RemoveListener(HandleGrabStart); baseInteractor.selectEntered.RemoveListener(HandleGrabStart);
baseInteractor.selectExited.RemoveListener(HandleGrabEnd); baseInteractor.selectExited.RemoveListener(HandleGrabEnd);
//interactor.activated.RemoveListener(OnActivated);
//interactor.deactivated.RemoveListener(OnDeactivated);
} }
} }
@@ -128,7 +142,13 @@ public class InteractionView : MonoBehaviour, IInteractionView
isGrabbingRobot = false; isGrabbingRobot = false;
currentGrabbedPointIndex = point.pointIndex; currentGrabbedPointIndex = point.pointIndex;
OnPointDragStart?.Invoke(currentGrabbedPointIndex); // 타이머 시작
startGrabPosition = rayProvider.rayEndPoint;
if (clickOrDragCoroutine != null)
StopCoroutine(clickOrDragCoroutine);
clickOrDragCoroutine = StartCoroutine(ClickOrDragTimer());
//OnPointDragStart?.Invoke(currentGrabbedPointIndex);
} }
else if (grabbedGO.CompareTag("RobotArm")) else if (grabbedGO.CompareTag("RobotArm"))
{ {
@@ -142,21 +162,57 @@ public class InteractionView : MonoBehaviour, IInteractionView
private void HandleGrabEnd(SelectExitEventArgs args) private void HandleGrabEnd(SelectExitEventArgs args)
{ {
if (isGrabbingPoint) if (isGrabbingRobot)
{
OnPointDragEnd?.Invoke(currentGrabbedPointIndex);
}
else if (isGrabbingRobot)
{ {
OnRobotReleased?.Invoke(new RobotData()); OnRobotReleased?.Invoke(new RobotData());
} }
else
{
if (clickOrDragCoroutine != null)
{
StopCoroutine(clickOrDragCoroutine);
clickOrDragCoroutine = null;
OnPointClicked?.Invoke(currentGrabbedPointIndex, currentTargetPosition);
}
else if (isGrabbingPoint)
{
OnPointDragEnd?.Invoke(currentGrabbedPointIndex);
}
}
// 상태 초기화 // 상태 초기화
clickOrDragCoroutine = null;
isGrabbingPoint = false; isGrabbingPoint = false;
isGrabbingRobot = false; isGrabbingRobot = false;
currentGrabbedPointIndex = -1; currentGrabbedPointIndex = -1;
} }
// 클릭/드래그 구분하는 타이머 코루틴
private IEnumerator ClickOrDragTimer()
{
yield return new WaitForSeconds(clickTimeThreshold);
if (clickOrDragCoroutine != null)
{
Debug.Log("시간 초과로 드래그 시작 (OnPointDragStart)");
StartDragMode();
}
}
// 타이머 중지하고 드래그 모드로 전환
private void StartDragMode()
{
if (clickOrDragCoroutine != null)
{
StopCoroutine(clickOrDragCoroutine);
clickOrDragCoroutine = null;
}
isGrabbingPoint = true;
OnPointDragStart?.Invoke(currentGrabbedPointIndex);
}
// 로봇 좌표계(mm)를 Unity 월드 좌표계(m)로 변환 // 로봇 좌표계(mm)를 Unity 월드 좌표계(m)로 변환
private Vector3 ConvertRobotDataToVector3(RobotData pose) private Vector3 ConvertRobotDataToVector3(RobotData pose)
{ {

View File

@@ -17,6 +17,11 @@ public class PointManagerView : MonoBehaviour, IPointManagerView
[Tooltip("반투명 로봇 모델의 IK")] [Tooltip("반투명 로봇 모델의 IK")]
public HybridInverseKinematicsNode kinematicsNode; public HybridInverseKinematicsNode kinematicsNode;
void Start()
{
movingAlert.SetActive(false);
}
private Vector3 ConvertRobotDataToVector3(RobotData pose) private Vector3 ConvertRobotDataToVector3(RobotData pose)
{ {
float x = Convert.ToSingle(pose.x / -1000.0); // mm -> m float x = Convert.ToSingle(pose.x / -1000.0); // mm -> m

View File

@@ -50,27 +50,38 @@ public class PopupView : MonoBehaviour, IPopupView
optionPopupPanel.SetActive(false); optionPopupPanel.SetActive(false);
} }
public void ShowConfirmPopup(Transform popPose) public void ShowConfirmPopupFromRobot(Transform popPose)
{ {
confirmPopupPanel.transform.SetPositionAndRotation(popPose.position, Quaternion.identity); confirmPopupPanel.transform.position = popPose.position;
confirmPopupPanel.transform.LookAt(Camera.main.transform);
confirmPopupPanel.SetActive(true);
}
public void ShowConfirmPopupFromPoint(Vector3 popPose)
{
confirmPopupPanel.transform.position = popPose;
confirmPopupPanel.transform.LookAt(Camera.main.transform);
confirmPopupPanel.SetActive(true); confirmPopupPanel.SetActive(true);
} }
public void ShowModifyPopup(Transform popPose) public void ShowModifyPopup(Transform popPose)
{ {
modifyPopupPanel.transform.SetPositionAndRotation(popPose.position, Quaternion.identity); modifyPopupPanel.transform.position = popPose.position;
modifyPopupPanel.transform.LookAt(Camera.main.transform);
modifyPopupPanel.SetActive(true); modifyPopupPanel.SetActive(true);
} }
public void ShowDeletePopup(Transform popPose) public void ShowDeletePopup(Vector3 popPose)
{ {
deletePopupPanel.transform.SetPositionAndRotation(popPose.position, Quaternion.identity); deletePopupPanel.transform.position = popPose;
deletePopupPanel.transform.LookAt(Camera.main.transform);
deletePopupPanel.SetActive(true); deletePopupPanel.SetActive(true);
} }
public void ShowOptionPopup(Transform popPose) public void ShowOptionPopup(Vector3 popPose)
{ {
optionPopupPanel.transform.SetPositionAndRotation(popPose.position, Quaternion.identity); optionPopupPanel.transform.position = popPose;
optionPopupPanel.transform.LookAt(Camera.main.transform);
optionPopupPanel.SetActive(true); optionPopupPanel.SetActive(true);
} }