using System; using System.Collections.Generic; using System.Net.Sockets; using System.Threading.Tasks; using UnityEngine; using UnityEngine.UIElements; using UnityEngine.XR.ARSubsystems; public enum PopupState { None, ConfirmAddPoint, ConfirmModifyPoint, MoveOrDelete, ConfirmDelete } public class ProgramPresenter { private ProgramModel model; private ProgramView view; private TCPView tcpView; private RobotController controlledRobot; private string _programId; private bool lastKnownMotorState = false; private InteractionView interactionView; private PointManagerView pointManagerView; private PathLineView pathLineView; private PopupView popupView; private PopupState currentPopupState = PopupState.None; private RobotData pendingPointData; // ÆË¾÷â¿¡ ´ëÇÑ ÀÀ´äÀ» ±â´Ù¸®´Â Àӽà µ¥ÀÌÅÍ private int activePointIndex = -1; // ÇöÀç ¼öÁ¤/»èÁ¦ ÁßÀÎ Æ÷ÀÎÆ®ÀÇ À妽º private bool IsDragging = false; public ProgramPresenter(ProgramModel model, ProgramView view, TCPView tcpView, InteractionView interactionView, PointManagerView pmView, PopupView popView, PathLineView pathLineView) { this.model = model; this.view = view; this.tcpView = tcpView; this.interactionView = interactionView; this.pointManagerView = pmView; this.popupView = popView; this.pathLineView = pathLineView; this.view.OnCreateProgramClicked += async (id) => await HandleCreateProgram(id); this.view.OnLoadProgramListRequested += HandleLoadProgramList; this.view.OnProgramSelectedToLoad += HandleProgramSelected; this.view.OnOpenProgramClicked += async () => await HandleOpenProgram(); this.view.OnSaveClicked += HandleSaveProgram; this.view.OnAddPointClicked += HandleAddPoint; this.tcpView.OnTCPupdateRequested += HandleTCPViewUpdate; this.interactionView.OnRobotGrabbed += HandleRobotGrabbed; this.interactionView.OnRobotReleased += HandleRobotReleased; //this.interactionView.OnPointClicked += HandlePointClicked; this.interactionView.OnPointDragStart += HandlePointDragStart; this.interactionView.OnPointDragUpdate += HandlePointDragUpdate; this.interactionView.OnPointDragEnd += HandlePointDragEnd; //this.popupView.OnPopupResponse += HandlePopupResponse; } public void RegisterControlledRobot(RobotController robot) { this.controlledRobot = robot; this.controlledRobot.OnPoseUpdateRequest += HandleGETPose; //this.controlledRobot.OnPoseUpdateReceive += HandlePOSTPose; } public async Task UpdateMotorStateAsync() { try { bool currentState = await model.GetRobotMotorStateAsync(); if (currentState != lastKnownMotorState) { controlledRobot.SetMotorState(currentState); lastKnownMotorState = currentState; } } catch (Exception e) { Debug.LogWarning($"¸ðÅÍ »óÅ ¾÷µ¥ÀÌÆ® ½ÇÆÐ: {e.Message}"); } } public void OnApplicationStart() { if (controlledRobot != null) { Debug.Log("Á¦¾îÇÒ ·Îº¿ÀÌ µî·ÏµÇ¾ú½À´Ï´Ù."); } else { Debug.LogError("Á¦¾îÇÒ ·Îº¿ÀÌ µî·ÏµÇÁö ¾Ê¾Ò½À´Ï´Ù"); } } private async Task HandleCreateProgram(string programId) { if (await model.CreateNewProgram(programId)) { view.DisplayProgram(programId); view.HideProgramSelectPanel(); OnApplicationStart(); } else { view.ShowMessage($"Error: Program ID '{programId}.job' already exists or is invalid."); } } private void HandleLoadProgramList() { var programIds = model.GetAllProgramIds(); view.ShowProgramList(programIds); } private void HandleProgramSelected(string programId) { _programId = programId; } private async Task HandleOpenProgram() { if(_programId != null && await model.LoadProgram(_programId)) { view.DisplayProgram(_programId); view.HideProgramSelectPanel(); view.HideProgramList(); OnApplicationStart(); _programId = null; } } private void HandleSaveProgram() { //model.SaveCurrentProgram(); } private void HandleAddPoint() { //// ½ÇÁ¦·Î´Â UI»óÀÇ ÁÂÇ¥³ª ·Îº¿ ¿£µåÆ÷ÀÎÆ® ÁÂÇ¥¸¦ ¹Þ¾Æ¿Í¾ß ÇÔ //// ¿©±â¼­´Â ¿¹½Ã·Î ·£´ý ÁÂÇ¥¸¦ »ç¿ë //Vector3 newPoint = new Vector3(Random.Range(-1f, 1f), Random.Range(0f, 1f), Random.Range(-1f, 1f)); //model.AddPointToCurrentProgram(newPoint); //view.DisplayProgram(model.CurrentProgram); } private void HandleTCPViewUpdate() { if (model.IsNewDataAvailable()) { RobotData data = model.GetLatestRobotData(); tcpView.SetCoordinates(data); } } // --- ½Ç½Ã°£ µ¿±âÈ­(Á¦¾î±â->3d) --- private void HandleGETPose() { //Debug.Log($"Á¦¾î±âµ¿±âÈ­ ¸ØÃã: {interactionView.isGrabbingRobot}"); //if (interactionView.isGrabbingRobot) // return; // 3d -> Á¦¾î±â Á¦¾î ÁßÀÌ¸é ¸ØÃã RobotData data = model.GetLatestRobotData(); if (data != null) { controlledRobot.DisableIK(); controlledRobot.SetRobotPosition(data); // 3D °¡»ó ·Îº¿ ¸ðµ¨ À§Ä¡ ¾÷µ¥ÀÌÆ® } } // --- ½Ç½Ã°£ µ¿±âÈ­(3d->Á¦¾î±â) --- private RobotData ConvertPoseToRobotData(Vector3 newWorldPos, Quaternion newWorldRot) { RobotData targetPose = new RobotData(); targetPose.x = Convert.ToSingle(Math.Round(-1 * newWorldPos.x * 1000, 2)); targetPose.y = Convert.ToSingle(Math.Round(-1 * newWorldPos.z * 1000, 2)); // Unity Z -> Robot Y targetPose.z = Convert.ToSingle(Math.Round(newWorldPos.y * 1000, 2)); // Unity Y -> Robot Z Vector3 eulerAngles = newWorldRot.eulerAngles; targetPose.rx = Convert.ToSingle(Math.Round(eulerAngles.x, 2)); targetPose.ry = Convert.ToSingle(Math.Round(eulerAngles.y, 2)); targetPose.rz = Convert.ToSingle(Math.Round(eulerAngles.z, 2)); return targetPose; } private async void HandleRobotGrabbed(Vector3 newWorldPos, Quaternion newWorldRot) { // controlledRobot.EnableIK(); RobotData newPose = ConvertPoseToRobotData(newWorldPos, newWorldRot); await model.StreamPoseUdpAsync(newPose); } // --- »õ Æ÷ÀÎÆ® Ãß°¡ --- private void HandleRobotReleased(RobotData pose) { IsDragging = false; //controlledRobot.DisableIK(); interactionView.isGrabbingRobot = false; HandleGETPose(); //pendingPointData = pose; // Àӽà ÀúÀå //currentPopupState = PopupState.ConfirmAddPoint; // »óÅ ¼³Á¤ //popupView.ShowConfirmPopup("À§Ä¡ È®Á¤", "ÀÌ À§Ä¡¸¦ »õ Æ÷ÀÎÆ®·Î ÀúÀåÇϽðڽÀ´Ï±î?"); // ÆË¾÷ ¿äû } // --- Æ÷ÀÎÆ® Ŭ¸¯ --- private void HandlePointClicked(int index) { activePointIndex = index; // À妽º ÀúÀå currentPopupState = PopupState.MoveOrDelete; // »óÅ ¼³Á¤ popupView.ShowOptionPopup("Æ÷ÀÎÆ® ÀÛ¾÷", "¹«¾ùÀ» ÇϽðڽÀ´Ï±î?", "¿©±â·Î À̵¿", "»èÁ¦"); // ÆË¾÷ ¿äû } // --- Æ÷ÀÎÆ® µå·¡±× --- private RobotData originalDragPose; // Ãë¼Ò ½Ã µ¹¾Æ°¥ ¿øº» À§Ä¡ private void HandlePointDragStart(int index) { IsDragging = true; controlledRobot.EnableIK(); activePointIndex = index; originalDragPose = model.CurrentProgram.GetStepPose(index); //interactionView.ShowDragArrow(GetPositionFromPose(originalDragPose)); interactionView.ShowGhostRobot(originalDragPose); } private async void HandlePointDragUpdate(int index, Vector3 newWorldPos) { ////if (!IsDragging) return; //RobotData newPose = ConvertVectorToRobotData(newWorldPos); //// °í½ºÆ® ·Îº¿, Æ÷ÀÎÆ®, °æ·Î ½Ç½Ã°£ À̵¿ ////interactionView.ShowGhostRobot(newPose); ////pointManagerView.UpdatePointPosition(index, newPose); ////pathLineView.DrawPath(GetFullPathOfProgramWithTempChange(index, newPose)); // Àӽà °æ·Î ±×¸®±â //await model.StreamPoseToRobotUdpAsync(newWorldPos); } private void HandlePointDragEnd(int index) { IsDragging = false; //interactionView.HideDragArrow(); //interactionView.HideGhostRobot(); //// (°¡»ó ·Îº¿ À§Ä¡ À̵¿ ·ÎÁ÷ - 5´Ü°è) //// robotController.SetRobotPosition(newPose); //pendingPointData = ConvertVectorToRobotData(GetLastDragPosition()); // Àӽà ÀúÀå //currentPopupState = PopupState.ConfirmModifyPoint; // »óÅ ¼³Á¤ //popupView.ShowConfirmPopup("À§Ä¡ ¼öÁ¤", "ÀÌ À§Ä¡·Î Æ÷ÀÎÆ®¸¦ ¼öÁ¤ÇϽðڽÀ´Ï±î?"); // ÆË¾÷ ¿äû } // --- ÆË¾÷ ÀÀ´ä ÅëÇÕ Çڵ鷯 --- private async void HandlePopupResponse(PopupResponse response) { popupView.HidePopup(); switch (currentPopupState) { case PopupState.ConfirmAddPoint: if (response == PopupResponse.Confirm) // È®Á¤ { await model.SavePointToProgramAsync(pendingPointData); RedrawSceneFromModel(); // ºä °»½Å } break; // µå·¡±× È®Á¤/Ãë¼Ò case PopupState.ConfirmModifyPoint: if (response == PopupResponse.Confirm) // È®Á¤ { // ÃÖÁ¾ À§Ä¡¸¦ ¼­¹ö¿¡ ÀúÀå // µå·¡±×°¡ ³¡³µÀ¸´Ï ÀÌ À§Ä¡¸¦ ÇÁ·Î±×·¥¿¡ ÀúÀå await model.SavePointToProgramAsync(pendingPointData, activePointIndex); RedrawSceneFromModel(); } else // Ãë¼Ò { pointManagerView.UpdatePointPosition(activePointIndex, originalDragPose); // ¿øÀ§Ä¡ RedrawSceneFromModel(); // °æ·Î ¿øÀ§Ä¡ } break; // À̵¿/»èÁ¦ case PopupState.MoveOrDelete: if (response == PopupResponse.Option1) // ¿©±â·Î À̵¿ { RobotData targetPose = model.CurrentProgram.GetStepPose(activePointIndex); //await model.StartMovement(targetPose); } else if (response == PopupResponse.Option2) // »èÁ¦ { currentPopupState = PopupState.ConfirmDelete; popupView.ShowConfirmPopup("»èÁ¦ È®ÀÎ", "Á¤¸»·Î ÀÌ Æ÷ÀÎÆ®¸¦ »èÁ¦ÇϽðڽÀ´Ï±î?"); } break; // ÃÖÁ¾ »èÁ¦ case PopupState.ConfirmDelete: if (response == PopupResponse.Confirm) { await model.DeletePointFromProgramAsync(activePointIndex); RedrawSceneFromModel(); } break; } // ¸ðµç ÀÛ¾÷ ÈÄ »óÅ ÃʱâÈ­ currentPopupState = PopupState.None; activePointIndex = -1; } // ModelÀÇ ÇöÀç »óŸ¦ ÀÐ¾î ¸ðµç View(Æ÷ÀÎÆ®, °æ·Î)¸¦ »õ·Î °íħ private void RedrawSceneFromModel() { //if (model.CurrentProgram == null) return; //// (RobotProgram.Steps (List)¸¦ List·Î º¯È¯ÇÏ´Â ·ÎÁ÷) //List poses = model.CurrentProgram.GetAllStepPoses(); //pointManagerView.RedrawPoints(poses); // Æ÷ÀÎÆ® ´Ù½Ã ±×¸² //pathLineView.DrawPath(poses); // °æ·Î ´Ù½Ã ±×¸² } private void Destroy() { this.view.OnCreateProgramClicked -= async (id) => await HandleCreateProgram(id); this.view.OnLoadProgramListRequested -= HandleLoadProgramList; this.view.OnProgramSelectedToLoad -= HandleProgramSelected; this.view.OnOpenProgramClicked -= async () => await HandleOpenProgram(); this.view.OnSaveClicked -= HandleSaveProgram; this.view.OnAddPointClicked -= HandleAddPoint; this.tcpView.OnTCPupdateRequested -= HandleTCPViewUpdate; this.controlledRobot.OnPoseUpdateRequest -= HandleGETPose; //this.controlledRobot.OnPoseUpdateReceive -= HandlePOSTPose; this.interactionView.OnRobotGrabbed -= HandleRobotGrabbed; this.interactionView.OnRobotReleased -= HandleRobotReleased; this.interactionView.OnPointDragStart -= HandlePointDragStart; this.interactionView.OnPointDragUpdate -= HandlePointDragUpdate; this.interactionView.OnPointDragEnd -= HandlePointDragEnd; } }