Files
HDRobotics/Assets/Scripts/AppManager.cs

250 lines
7.7 KiB
C#

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, string>();
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();
}
}
}