<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;
transform.SetParent(attachParent);
transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
// 부착된 동안 물리적 충돌이나 중력을 무시하도록 Rigidbody를 Kinematic으로 설정
if (rb != null)

View File

@@ -524,6 +524,9 @@ public class ProgramModel : IProgramModel
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.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;
@@ -76,6 +77,9 @@ public class ProgramPresenter
this.controlledRobot = robot;
this.controlledRobot.OnPoseUpdateRequest += HandleGETPose;
popPos = controlledRobot.GetEndPoint();
if (controlledRobot != null)
controlledRobot.StartCoroutine(UpdateRobotMovingStateCoroutine());
}
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()
{
if (controlledRobot != null)
@@ -220,7 +255,7 @@ public class ProgramPresenter
this.latestTargetPose = ConvertPoseToRobotData(newWorldPos, newWorldRot);
await model.MoveToPoseTcpAsync(newWorldPos);
await model.MoveToPoseTcpAsync(newWorldPos);
}
catch (Exception e)
{
@@ -232,9 +267,10 @@ public class ProgramPresenter
private void HandleRobotReleased(RobotData pose)
{
IsDragging = false;
pendingPointData = pose; // 임시 저장
pendingPointData = this.latestTargetPose; // 임시 저장
currentPopupState = PopupState.ConfirmAddPoint; // 상태 설정
popupView.ShowConfirmPopup(popPos); // 팝업 요청
_ = WaitForRobotToStopAndShowPopup(currentPopupState);
}
private void StopDragMoveLoop()
@@ -245,11 +281,11 @@ public class ProgramPresenter
}
// --- 포인트 클릭 ---
private void HandlePointClicked(int index)
private void HandlePointClicked(int index, Vector3 newWorldPos)
{
activePointIndex = index; // 인덱스 저장
currentPopupState = PopupState.MoveOrDelete; // 상태 설정
popupView.ShowOptionPopup(popPos); // 팝업 요청
popupView.ShowOptionPopup(newWorldPos); // 팝업 요청
}
// --- 포인트 드래그 ---
@@ -283,7 +319,7 @@ public class ProgramPresenter
RedrawTemporaryPath(index, latestTargetPose);
}
private void HandlePointDragEnd(int index)
private async void HandlePointDragEnd(int index)
{
IsDragging = false;
@@ -291,10 +327,14 @@ public class ProgramPresenter
interactionView.HideGhostRobot();
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)
@@ -307,7 +347,7 @@ public class ProgramPresenter
case PopupState.ConfirmAddPoint:
if (response == PopupResponse.InsConfirm)
{
await model.SavePointToProgramAsync(pendingPointData);
await model.SavePointToProgramAsync(pendingPointData, -1);
RedrawSceneFromModel(); // 뷰 갱신
}
break;
@@ -323,27 +363,23 @@ public class ProgramPresenter
else if (response == PopupResponse.Cancel)
{
pointManagerView.UpdatePointPosition(activePointIndex, originalDragPose); // 원위치
RedrawSceneFromModel(); // 경로 원위치
RedrawTemporaryPath(activePointIndex, originalDragPose); // 경로 원위치
}
break;
// 여기로 이동/삭제
case PopupState.MoveOrDelete:
RobotData targetPose = model.CurrentProgram.GetStepPose(activePointIndex);
Vector3 pos = ConvertRobotDataToVector3(targetPose);
if (response == PopupResponse.Move)
{
RobotData targetPose = model.CurrentProgram.GetStepPose(activePointIndex);
Vector3 pos = ConvertRobotDataToVector3(targetPose);
// 알림 UI 표시
pointManagerView.movingAlert.SetActive(true);
await model.MoveToPoseTcpAsync(pos);
Debug.Log($"이동 중? : { await model.GetMovingState()}"); // TODO. 어떤 값을 받아오는지.
}
else if (response == PopupResponse.Delete)
{
popupView.ShowDeletePopup(pos);
currentPopupState = PopupState.ConfirmDelete;
popupView.ShowConfirmPopup(popPos);
return;
}
break;
@@ -362,10 +398,42 @@ public class ProgramPresenter
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를 새로 고침
private void RedrawSceneFromModel()
private async void RedrawSceneFromModel()
{
if (model.CurrentProgram == null) return;
await model.LoadProgram(model.CurrentProgram.ProgramId);
// RobotProgram.Steps (List<RobotMoveStep>)를 List<RobotData>로 변환
List<RobotData> poses = model.CurrentProgram.GetAllStepPoses();

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
@@ -15,7 +16,7 @@ public class InteractionView : MonoBehaviour, IInteractionView
{
public event Action<Vector3, Quaternion> OnRobotGrabbed;
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, Vector3, Quaternion> OnPointDragUpdate;
public event Action<int> OnPointDragEnd;
@@ -30,11 +31,18 @@ public class InteractionView : MonoBehaviour, IInteractionView
[SerializeField]
[Tooltip("드래그용 마우스 이미지")]
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 isGrabbingPoint = false;
private int currentGrabbedPointIndex = -1;
Vector3 currentTargetPosition = Vector3.zero;
Quaternion currentTargetRotation = Quaternion.identity;
public bool isGrabbingRobot = false;
void Start()
@@ -76,21 +84,29 @@ public class InteractionView : MonoBehaviour, IInteractionView
}
else
{
Vector3 currentTargetPosition = Vector3.zero;
Quaternion currentTargetRotation = Quaternion.identity;
currentTargetPosition = rayProvider.rayEndPoint;
currentTargetRotation = baseInteractor.attachTransform.rotation;
if (isGrabbingRobot)
{
OnRobotGrabbed?.Invoke(currentTargetPosition, currentTargetRotation);
return;
}
if (isGrabbingPoint)
{
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.selectExited.RemoveListener(HandleGrabEnd);
//interactor.activated.RemoveListener(OnActivated);
//interactor.deactivated.RemoveListener(OnDeactivated);
}
}
@@ -128,7 +142,13 @@ public class InteractionView : MonoBehaviour, IInteractionView
isGrabbingRobot = false;
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"))
{
@@ -142,21 +162,57 @@ public class InteractionView : MonoBehaviour, IInteractionView
private void HandleGrabEnd(SelectExitEventArgs args)
{
if (isGrabbingPoint)
{
OnPointDragEnd?.Invoke(currentGrabbedPointIndex);
}
else if (isGrabbingRobot)
if (isGrabbingRobot)
{
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;
isGrabbingRobot = false;
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)로 변환
private Vector3 ConvertRobotDataToVector3(RobotData pose)
{

View File

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

View File

@@ -50,27 +50,38 @@ public class PopupView : MonoBehaviour, IPopupView
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);
}
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);
}
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);
}
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);
}