using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Threading; using UnityEngine; public class AppManager : MonoBehaviour { public static AppManager Instance { get; private set; } [SerializeField] private ProgramView view; [SerializeField] private TCPView tcpView; [SerializeField] private RobotInfoView robotInfoView; [SerializeField] private ProgramInfoView programInfoView; [SerializeField] private EnvView envView; [SerializeField] private GripperCollide gripperCollide; [SerializeField] private RobotController robotController; private InteractionView leftInteractionView; private InteractionView rightInteractionView; [SerializeField] private PointManagerView pointManagerView; [SerializeField] private PathLineView pathLineView; [SerializeField] private PopupView popupView; [SerializeField] private float motorStatePollInterval = 1.0f; [SerializeField] private AudioSource robotAudio; public CancellationTokenSource cancellationTokenSource; private bool isModelAndStaticViewsReady = false; private ProgramModel model; private ProgramPresenter presenter; private string hostip; private int tcpPort; private int udpPort; private string configFileName = "config.cfg"; void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); } else { Instance = this; } // RTT °¨¼Ò ¼³Á¤ // Nagle ¾Ë°í¸®Áò ºñȰ¼ºÈ­ (µ¥ÀÌÅÍ ¸ðÀ¸Áö ¸»°í Áï½Ã Àü¼Û) System.Net.ServicePointManager.UseNagleAlgorithm = false; // 100-Continue ´ë±â Á¦°Å (Çì´õ º¸³»°í ¼­¹ö Çã¶ô ±â´Ù¸®´Â °úÁ¤ »ý·«) System.Net.ServicePointManager.Expect100Continue = false; // µ¿½Ã ¿¬°á ¼ö Áõ°¡ (º´¸ñ Çö»ó ¹æÁö) System.Net.ServicePointManager.DefaultConnectionLimit = 10; } async void Start() { LoadConfig(); model = new ProgramModel(hostip, tcpPort, udpPort, robotController); await model.InitializeAsync(); if (view == null || tcpView == null || robotInfoView == null || programInfoView == null || envView == null || robotController == null || pointManagerView == null || popupView == null || pathLineView == null) { Debug.LogError("AppManagerÀÇ ÀνºÆåÅÍ¿¡ [Static Views]°¡ ¸ðµÎ ÇÒ´çµÇÁö ¾ÊÀ½", this); return; } isModelAndStaticViewsReady = true; TryCreatePresenter(); } public void RegisterView(InteractionView Iview) { if (Iview.handSide == HandSide.Left) { this.leftInteractionView = Iview; } else if (Iview.handSide == HandSide.Right) { this.rightInteractionView = Iview; } TryCreatePresenter(); } private void TryCreatePresenter() { if (presenter != null) return; if (!isModelAndStaticViewsReady || leftInteractionView == null || rightInteractionView == null) { return; } cancellationTokenSource = new CancellationTokenSource(); try { presenter = new ProgramPresenter( model, view, tcpView, robotInfoView, programInfoView, envView, gripperCollide, leftInteractionView, rightInteractionView, pointManagerView, popupView, pathLineView, robotAudio, cancellationTokenSource ); } catch (Exception e) { Debug.LogError($"Presenter »ý¼ºÀÚ¿¡¼­ ¿À·ù ¹ß»ý: {e.Message}\n{e.StackTrace}"); return; // »ý¼º ½ÇÆÐ } presenter.RegisterControlledRobot(robotController); _ = presenter.UpdateMotorStateAsync(); _ = model.GetTCPAsync(cancellationTokenSource.Token); _ = model.StartMovementCheckLoopAsync(cancellationTokenSource.Token); _ = model.GetMovementState(cancellationTokenSource.Token); view.DisplayProgram(null); StartCoroutine(PollMotorStateCoroutine()); } private void OnDestroy() { if (Instance == this) { cancellationTokenSource?.Cancel(); cancellationTokenSource?.Dispose(); } } private void LoadConfig() { // ±âº»°ª ¼³Á¤ (ÆÄÀÏÀ» ¸ø ãÀ» °æ¿ì ´ëºñ) string defaultIp = "127.0.0.1"; int defaultPort = 8888; string path = Path.Combine(Application.streamingAssetsPath, configFileName); if (File.Exists(path)) { try { var config = new Dictionary(); string[] lines = File.ReadAllLines(path); foreach (string line in lines) { if (string.IsNullOrWhiteSpace(line) || line.Trim().StartsWith("#")) continue; string[] parts = line.Split('='); if (parts.Length == 2) { config[parts[0].Trim()] = parts[1].Trim(); } } if (config.ContainsKey("IP_ADDRESS")) { hostip = config["IP_ADDRESS"]; } else { hostip = defaultIp; Debug.LogWarning($"config ÆÄÀÏ¿¡ IP_ADDRESS ۰¡ ¾ø½À´Ï´Ù. ±âº»°ª({defaultIp}) »ç¿ë."); } if (config.ContainsKey("TCP_PORT")) { if (int.TryParse(config["TCP_PORT"], out int parsedPort)) { tcpPort = parsedPort; } else { tcpPort = defaultPort; Debug.LogWarning($"config ÆÄÀÏÀÇ TCP_PORT °ªÀÌ À߸øµÇ¾ú½À´Ï´Ù. ±âº»°ª({defaultPort}) »ç¿ë."); } } else { tcpPort = defaultPort; Debug.LogWarning($"config ÆÄÀÏ¿¡ TCP_PORT ۰¡ ¾ø½À´Ï´Ù. ±âº»°ª({defaultPort}) »ç¿ë."); } if (config.ContainsKey("UDP_PORT")) { if (int.TryParse(config["UDP_PORT"], out int parsedPort)) { udpPort = parsedPort; } else { udpPort = defaultPort; Debug.LogWarning($"config ÆÄÀÏÀÇ UDP_PORT °ªÀÌ À߸øµÇ¾ú½À´Ï´Ù. ±âº»°ª({defaultPort}) »ç¿ë."); } } else { udpPort = defaultPort; Debug.LogWarning($"config ÆÄÀÏ¿¡ UDP_PORT ۰¡ ¾ø½À´Ï´Ù. ±âº»°ª({defaultPort}) »ç¿ë."); } Debug.Log($"Config ·Îµå ¼º°ø: {hostip}:{tcpPort}/{udpPort}"); } catch (System.Exception e) { Debug.LogError($"Config ÆÄÀÏ ·Îµå Áß ¿À·ù ¹ß»ý: {e.Message}. ±âº»°ª »ç¿ë."); hostip = defaultIp; tcpPort = defaultPort; udpPort = defaultPort; } } else { Debug.LogWarning($"{configFileName} ÆÄÀÏÀ» ãÀ» ¼ö ¾ø½À´Ï´Ù. ±âº»°ª({defaultIp}:{defaultPort}) »ç¿ë."); hostip = defaultIp; tcpPort = defaultPort; udpPort = defaultPort; } } private IEnumerator PollMotorStateCoroutine() { while (true) { yield return new WaitForSeconds(motorStatePollInterval); _ = presenter.UpdateMotorStateAsync(); } } }