RomaWifiCameraControlClient.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. using System;
  2. using System.Net;
  3. using System.Net.Sockets;
  4. using System.Text;
  5. using LightGlue.Unity.Config;
  6. using UnityEngine;
  7. namespace LightGlue.Unity.Roma
  8. {
  9. /// <summary>
  10. /// OrangePi WiFi 图传控制(JSON over UDP,端口默认 8008)。
  11. /// 参考:orangepizero2w/UDP_PROTOCOL.md、上位机参考.md
  12. /// </summary>
  13. public sealed class RomaWifiCameraControlClient : MonoBehaviour
  14. {
  15. [Header("Device")]
  16. [Tooltip("设备IP(从广播发现获取后写入)")]
  17. public string deviceIp = "";
  18. [Tooltip("设备控制端口(固定 8008)")]
  19. public int deviceControlPort = 8008;
  20. [Header("Target (PC / Python)")]
  21. [Tooltip("图像流目标IP(通常是本机IP,不要用127.0.0.1)")]
  22. public string targetIp = "";
  23. [Tooltip("图像流目标端口(Python WiFi 图传接收端口)。注意不要填 12345(广播端口)")]
  24. public int targetPort = 9000;
  25. [Header("Behavior")]
  26. [Tooltip("发现设备IP后,是否自动发送 config + stream_on")]
  27. public bool autoConfigAndStartOnDeviceDiscovered = false;
  28. [Tooltip("若启用自动下发,是否由 RomaNetworkConfigUIController 统一执行(确保使用 localTargetIpField)。建议保持 true。")]
  29. public bool autoConfigHandledByUi = true;
  30. [Tooltip("当 targetIp 为空或为 127.0.0.1 时,是否自动填充为本机网卡 IP(用于下发给设备)。")]
  31. public bool autoFillTargetIpIfEmpty = true;
  32. [Tooltip("发送 config 后等待的毫秒数(给设备一点应用时间)")]
  33. public int delayMsBetweenConfigAndOn = 30;
  34. public event Action<string, int> OnConfigSent;
  35. public event Action OnStreamOnSent;
  36. private UdpClient _client;
  37. private IPEndPoint _deviceEp;
  38. private void OnEnable()
  39. {
  40. EnsureClient();
  41. }
  42. private void OnDisable()
  43. {
  44. try { _client?.Close(); } catch { /* ignore */ }
  45. try { _client?.Dispose(); } catch { /* ignore */ }
  46. _client = null;
  47. _deviceEp = null;
  48. }
  49. public void BindDiscovery(RomaDeviceDiscoveryListener discovery)
  50. {
  51. if (discovery == null) return;
  52. discovery.OnDeviceIpDiscovered -= HandleDeviceDiscovered;
  53. discovery.OnDeviceIpDiscovered += HandleDeviceDiscovered;
  54. }
  55. private void HandleDeviceDiscovered(string ip)
  56. {
  57. deviceIp = ip;
  58. Debug.Log($"[RomaWiFiCtrl] Device discovered: {deviceIp}");
  59. if (!autoConfigAndStartOnDeviceDiscovered) return;
  60. if (autoConfigHandledByUi) return;
  61. if (autoFillTargetIpIfEmpty && (string.IsNullOrWhiteSpace(targetIp) || targetIp.Trim() == "127.0.0.1"))
  62. {
  63. string localIp = NetworkConfig.GetLocalBindIp();
  64. if (!string.IsNullOrWhiteSpace(localIp) && localIp != "127.0.0.1")
  65. {
  66. targetIp = localIp;
  67. Debug.Log($"[RomaWiFiCtrl] Auto filled targetIp: {targetIp}");
  68. }
  69. else
  70. {
  71. Debug.LogWarning("[RomaWiFiCtrl] targetIp 为空/127.0.0.1,且未能获取有效本机IP;将不会自动下发 config,请在 UI 中填写本机IP 后再发送。");
  72. return;
  73. }
  74. }
  75. ConfigStream(targetIp, targetPort);
  76. if (delayMsBetweenConfigAndOn > 0)
  77. {
  78. Invoke(nameof(StartStream), delayMsBetweenConfigAndOn / 1000f);
  79. }
  80. else
  81. {
  82. StartStream();
  83. }
  84. }
  85. public void ConfigStream(string newTargetIp, int newTargetPort)
  86. {
  87. // 允许在配置时自动补齐本机IP,避免 Validate 直接失败导致用户误以为“没发送”
  88. if (autoFillTargetIpIfEmpty && (string.IsNullOrWhiteSpace(targetIp) || targetIp.Trim() == "127.0.0.1"))
  89. {
  90. string localIp = NetworkConfig.GetLocalBindIp();
  91. if (!string.IsNullOrWhiteSpace(localIp) && localIp != "127.0.0.1")
  92. targetIp = localIp;
  93. }
  94. if (!Validate()) return;
  95. if (!string.IsNullOrWhiteSpace(newTargetIp))
  96. targetIp = newTargetIp.Trim();
  97. if (newTargetPort > 0)
  98. targetPort = newTargetPort;
  99. string json = $"{{\"type\":\"config\",\"data\":{{\"target_ip\":\"{targetIp}\",\"target_port\":{targetPort}}}}}";
  100. SendJson(json);
  101. Debug.Log($"[RomaWiFiCtrl] Sent config: target={targetIp}:{targetPort}");
  102. OnConfigSent?.Invoke(targetIp, targetPort);
  103. }
  104. public void StartStream()
  105. {
  106. if (!Validate()) return;
  107. SendJson("{\"type\":\"stream_on\",\"data\":{}}");
  108. Debug.Log("[RomaWiFiCtrl] Sent stream_on");
  109. OnStreamOnSent?.Invoke();
  110. }
  111. public void StopStream()
  112. {
  113. if (!Validate()) return;
  114. SendJson("{\"type\":\"stream_off\",\"data\":{}}");
  115. Debug.Log("[RomaWiFiCtrl] Sent stream_off");
  116. }
  117. private void SendJson(string json)
  118. {
  119. EnsureClient();
  120. if (_client == null) return;
  121. try
  122. {
  123. _deviceEp = new IPEndPoint(IPAddress.Parse(deviceIp), deviceControlPort);
  124. byte[] payload = Encoding.UTF8.GetBytes(json);
  125. _client.Send(payload, payload.Length, _deviceEp);
  126. }
  127. catch (Exception ex)
  128. {
  129. Debug.LogWarning($"[RomaWiFiCtrl] Send failed: {ex.Message}");
  130. }
  131. }
  132. private bool Validate()
  133. {
  134. if (string.IsNullOrWhiteSpace(deviceIp))
  135. {
  136. Debug.LogWarning("[RomaWiFiCtrl] deviceIp 为空(尚未发现设备)");
  137. return false;
  138. }
  139. if (deviceControlPort <= 0 || deviceControlPort > 65535)
  140. {
  141. Debug.LogWarning($"[RomaWiFiCtrl] deviceControlPort 无效: {deviceControlPort}");
  142. return false;
  143. }
  144. if (string.IsNullOrWhiteSpace(targetIp))
  145. {
  146. Debug.LogWarning("[RomaWiFiCtrl] targetIp 为空(请填写本机IP)");
  147. return false;
  148. }
  149. if (targetPort <= 0 || targetPort > 65535)
  150. {
  151. Debug.LogWarning($"[RomaWiFiCtrl] targetPort 无效: {targetPort}");
  152. return false;
  153. }
  154. if (targetPort == 12345)
  155. {
  156. Debug.LogWarning("[RomaWiFiCtrl] targetPort=12345 是广播端口,建议不要用(会冲突/混乱)。请改为 9000 或其他端口。");
  157. }
  158. return true;
  159. }
  160. private void EnsureClient()
  161. {
  162. if (_client != null) return;
  163. try
  164. {
  165. _client = new UdpClient();
  166. }
  167. catch (Exception ex)
  168. {
  169. Debug.LogError($"[RomaWiFiCtrl] Create UdpClient failed: {ex.Message}");
  170. _client = null;
  171. }
  172. }
  173. }
  174. }