using System; using LightGlue.Unity.Networking; using LightGlue.Unity.Python; using LightGlue.Unity.Roma.Bridge; using LightGlue.Unity.Roma.Networking; using UnityEngine; namespace LightGlue.Unity.Roma { /// /// Roma 全局管理器(跨场景单例,DontDestroyOnLoad)。 /// 目的:在切换游戏场景时,Roma 的发现/图传配置/Python进程/结果接收/转发显示能持续运行。 /// public sealed class RomaManager : MonoBehaviour { public static RomaManager Instance { get; private set; } [Header("Roma 核心组件(建议挂在同一 GameObject 上)")] public RomaDeviceDiscoveryListener discovery; public RomaWifiCameraControlClient wifiControl; public PythonProcessController romaPythonController; public RomaForwardedImageViewer forwardedViewer; public RomaDeviceInfoReceiver deviceInfoReceiver; [Header("启动策略(手动模式建议全部关闭)")] [Tooltip("是否在 RomaManager.OnEnable 自动开始监听广播(12345)。")] public bool autoStartDiscoveryOnEnable = false; [Tooltip("是否在 RomaManager.OnEnable 自动启动 Roma Python 进程。")] public bool autoStartPythonOnEnable = false; [Tooltip("是否在 RomaManager.OnEnable 自动启动 Python->Unity 图像转发接收(12366)。")] public bool autoStartForwardViewerOnEnable = false; [Tooltip("是否在 RomaManager.OnEnable 自动启动结果接收(12348)。")] public bool autoStartResultReceiverOnEnable = false; [Header("Roma 结果接收 (Python -> Unity)")] public bool enableResultReceiver = true; public string resultBindIp = "127.0.0.1"; public int resultPort = 12348; public int maxResultQueueSize = 10; private UDPResultReceiver _resultReceiver; private RomaHardwareToPythonBridge _bridge; public UDPResultReceiver ResultReceiver => _resultReceiver; public event Action OnResultUpdated; private LightGlueResult _latest; private bool _hasLatest; public bool HasLatestResult => _hasLatest; public LightGlueResult LatestResult => _latest; private void Awake() { if (Instance != null && Instance != this) { gameObject.SetActive(false); Destroy(gameObject); return; } Instance = this; DontDestroyOnLoad(gameObject); if (discovery == null) discovery = GetComponent(); if (wifiControl == null) wifiControl = GetComponent(); if (romaPythonController == null) romaPythonController = GetComponent(); if (forwardedViewer == null) forwardedViewer = GetComponent(); if (deviceInfoReceiver == null) deviceInfoReceiver = GetComponent(); if (wifiControl != null && discovery != null) wifiControl.BindDiscovery(discovery); } private void OnDestroy() { if (Instance == this) Instance = null; } private void OnEnable() { Application.runInBackground = true; // 若场景中存在 RomaHardwareToPythonBridge,则由它负责接收结果,RomaManager 仅做统一对外分发 TryBindBridgeAsResultSource(); // 设备信息接收器:用于 ESP32 无广播时自动填充 device_ip/device_port if (deviceInfoReceiver != null) deviceInfoReceiver.StartListening(); if (autoStartDiscoveryOnEnable) StartDiscovery(); if (autoStartPythonOnEnable) StartPython(); if (autoStartForwardViewerOnEnable) StartForwardViewer(); // 仅当没有桥接层接管结果接收时,才由 RomaManager 自己启动 UDPResultReceiver if (_bridge == null && autoStartResultReceiverOnEnable && enableResultReceiver) StartResultReceiver(); } private void OnDisable() { StopResultReceiver(); _hasLatest = false; if (_bridge != null) { _bridge.OnResultUpdated -= OnBridgeResultUpdated; _bridge = null; } } private void Update() { // 若桥接层存在,则结果通过桥接层事件推送,这里无需再 drain 自己的 UDP 队列 if (_bridge != null) return; if (_resultReceiver == null) return; // 排空队列,只保留最新 bool any = false; LightGlueResult last = default; while (_resultReceiver.TryDequeueResult(out var r)) { last = r; any = true; } if (any) { _latest = last; _hasLatest = true; OnResultUpdated?.Invoke(last); } } public bool TryGetLatestResult(out LightGlueResult result) { result = default; if (!_hasLatest) return false; result = _latest; return true; } public void StartDiscovery() { if (discovery == null) discovery = GetComponent() ?? FindObjectOfType(); if (discovery != null && !discovery.IsListening) discovery.StartListening(); } public void StopDiscovery() { if (discovery == null) discovery = GetComponent() ?? FindObjectOfType(); discovery?.StopListening(); } public void StartPython() { if (romaPythonController == null) romaPythonController = GetComponent() ?? FindObjectOfType(); if (romaPythonController != null && !romaPythonController.IsRunning) romaPythonController.StartPython(); } public void StopPython() { if (romaPythonController == null) romaPythonController = GetComponent() ?? FindObjectOfType(); if (romaPythonController != null && romaPythonController.IsRunning) romaPythonController.StopPython(); } public void StartForwardViewer() { if (forwardedViewer == null) forwardedViewer = GetComponent() ?? FindObjectOfType(); forwardedViewer?.StartReceiver(); } public void StopForwardViewer() { if (forwardedViewer == null) forwardedViewer = GetComponent() ?? FindObjectOfType(); forwardedViewer?.StopReceiver(); } public void StartResultReceiver() { if (!enableResultReceiver) return; if (_resultReceiver != null) return; if (_bridge != null) return; // 避免与桥接层抢占端口 TryStartResultReceiver(); } public void StopResultReceiver() { try { _resultReceiver?.Stop(); } catch { /* ignore */ } _resultReceiver = null; } private void TryStartResultReceiver() { try { _resultReceiver = new UDPResultReceiver(resultBindIp, resultPort, maxResultQueueSize); _resultReceiver.Start(); Debug.Log($"[RomaManager] Result receiver started on {resultBindIp}:{resultPort}"); } catch (Exception ex) { Debug.LogError($"[RomaManager] Start result receiver failed on {resultBindIp}:{resultPort}: {ex.Message}"); _resultReceiver = null; } } private void TryBindBridgeAsResultSource() { if (_bridge != null) return; _bridge = FindObjectOfType(); if (_bridge == null) return; _bridge.OnResultUpdated -= OnBridgeResultUpdated; _bridge.OnResultUpdated += OnBridgeResultUpdated; if (_bridge.HasLatestResult) { OnBridgeResultUpdated(_bridge.LatestResult); } Debug.Log("[RomaManager] Using RomaHardwareToPythonBridge as result source."); } private void OnBridgeResultUpdated(LightGlueResult result) { _latest = result; _hasLatest = true; OnResultUpdated?.Invoke(result); } } }