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 RobotController robotController; private InteractionView interactionView; [SerializeField] private PointManagerView pointManagerView; [SerializeField] private PathLineView pathLineView; [SerializeField] private PopupView popupView; [SerializeField] private float motorStatePollInterval = 1.0f; 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; } } async void Start() { LoadConfig(); model = new ProgramModel(hostip, tcpPort, udpPort, robotController); await model.InitializeAsync(); isModelAndStaticViewsReady = true; TryCreatePresenter(); } public void RegisterView(InteractionView Iview) { if (this.interactionView != null) return; this.interactionView = Iview; TryCreatePresenter(); } private void TryCreatePresenter() { if (presenter != null) return; presenter = new ProgramPresenter( model, view, tcpView, interactionView, pointManagerView, popupView, pathLineView ); presenter.RegisterControlledRobot(robotController); _ = presenter.UpdateMotorStateAsync(); cancellationTokenSource = new CancellationTokenSource(); _ = model.GetTCPAsync(cancellationTokenSource.Token); _ = model.StartMovementCheckLoopAsync(cancellationTokenSource.Token); view.DisplayProgram(null); StartCoroutine(PollMotorStateCoroutine()); } 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(); } } }