|
| 1 | +using System.Collections.Generic; |
| 2 | +using UnityEngine; |
| 3 | + |
| 4 | +/// <summary> |
| 5 | +/// TCPListener 模式模擬情境 — Unity 端 |
| 6 | +/// |
| 7 | +/// 對應情境: |
| 8 | +/// FakeArduino ──TCP──▶ EdgeLink Port 8888 (TCP Server) |
| 9 | +/// ↓ 路由轉發 |
| 10 | +/// EdgeLink Port 9001 (TCP Client) ──TCP──▶ Unity(此腳本) |
| 11 | +/// |
| 12 | +/// 設定步驟: |
| 13 | +/// 1. 在此 GameObject 上加入 EdgeLinkManager 組件 |
| 14 | +/// 2. Inspector 設定: |
| 15 | +/// Protocol = TCPListener |
| 16 | +/// Listen Port = 9001 |
| 17 | +/// Device Id Key = id |
| 18 | +/// Device Timeout = 20 |
| 19 | +/// 3. EdgeLink Web UI 新增: |
| 20 | +/// TCP Server Port 8888(等 Arduino 連入) |
| 21 | +/// TCP Client Port 9001,Host = Unity 主機 IP,SourceProtocolId = 8888 ID |
| 22 | +/// 4. 執行 FakeArduino(SDK/CSharp/examples/Simulate_TCPListener/FakeArduino) |
| 23 | +/// </summary> |
| 24 | +public class SimulateTCPListener : MonoBehaviour |
| 25 | +{ |
| 26 | + EdgeLinkManager edgeLink; |
| 27 | + string lastRaw; |
| 28 | + |
| 29 | + // 顯示用 |
| 30 | + readonly List<string> log = new(); |
| 31 | + readonly Dictionary<string, DeviceState> devices = new(); |
| 32 | + const int MAX_LOG = 12; |
| 33 | + |
| 34 | + class DeviceState |
| 35 | + { |
| 36 | + public string Id; |
| 37 | + public string Temp; |
| 38 | + public string Humid; |
| 39 | + public string Seq; |
| 40 | + public bool IsOnline = true; |
| 41 | + public float LastSeen; |
| 42 | + } |
| 43 | + |
| 44 | + void Start() |
| 45 | + { |
| 46 | + edgeLink = GetComponent<EdgeLinkManager>(); |
| 47 | + if (edgeLink == null) |
| 48 | + { |
| 49 | + Debug.LogError("[Simulate] 找不到 EdgeLinkManager,請將此腳本與 EdgeLinkManager 掛在同一個 GameObject"); |
| 50 | + return; |
| 51 | + } |
| 52 | + |
| 53 | + // ── EdgeLink ↔ Unity 連線狀態 ────────────────────────────────────────── |
| 54 | + // 這代表 EdgeLink Server 本身是否連入 Unity |
| 55 | + // (TCPListener 模式下,EdgeLink 是主動連進來的那方) |
| 56 | + |
| 57 | + // ── 上游設備(ESP32)連線 / 斷線(TCP,拔電後約 15 秒偵測到)────────── |
| 58 | + edgeLink.OnDeviceStatus += (connected, endpoint) => |
| 59 | + { |
| 60 | + string ip = endpoint.Contains("@") ? endpoint.Split('@')[1] : endpoint; |
| 61 | + if (connected) |
| 62 | + AddLog($"<color=#55ff55>▲ 設備上線 IP: {ip}</color>"); |
| 63 | + else |
| 64 | + AddLog($"<color=#ff5555>▼ 設備斷線 IP: {ip}</color>"); |
| 65 | + }; |
| 66 | + |
| 67 | + // ── Timeout 偵測(TCP/UDP 通用,依 id 欄位計時)────────────────────── |
| 68 | + edgeLink.OnDeviceTimeout += deviceId => |
| 69 | + { |
| 70 | + AddLog($"<color=#ffaa00>⚠ {deviceId} 超時,可能已離線</color>"); |
| 71 | + if (devices.TryGetValue(deviceId, out var d)) d.IsOnline = false; |
| 72 | + }; |
| 73 | + |
| 74 | + // ── 超時設備重新傳資料 ──────────────────────────────────────────────── |
| 75 | + edgeLink.OnDeviceReconnected += deviceId => |
| 76 | + { |
| 77 | + AddLog($"<color=#55ffff>↑ {deviceId} 重新上線</color>"); |
| 78 | + if (devices.TryGetValue(deviceId, out var d)) d.IsOnline = true; |
| 79 | + }; |
| 80 | + } |
| 81 | + |
| 82 | + void Update() |
| 83 | + { |
| 84 | + if (edgeLink == null || edgeLink.Raw == lastRaw) return; |
| 85 | + lastRaw = edgeLink.Raw; |
| 86 | + |
| 87 | + string id = edgeLink.Get("id"); |
| 88 | + string temp = edgeLink.Get("temp"); |
| 89 | + string humid = edgeLink.Get("humid"); |
| 90 | + string seq = edgeLink.Get("seq"); |
| 91 | + |
| 92 | + if (id == null) return; |
| 93 | + |
| 94 | + if (!devices.TryGetValue(id, out var device)) |
| 95 | + { |
| 96 | + device = new DeviceState { Id = id }; |
| 97 | + devices[id] = device; |
| 98 | + } |
| 99 | + |
| 100 | + device.Temp = temp; |
| 101 | + device.Humid = humid; |
| 102 | + device.Seq = seq; |
| 103 | + device.IsOnline = true; |
| 104 | + device.LastSeen = Time.time; |
| 105 | + |
| 106 | + AddLog($"[{id}] seq={seq} temp={temp}°C humid={humid}%"); |
| 107 | + } |
| 108 | + |
| 109 | + void AddLog(string msg) |
| 110 | + { |
| 111 | + log.Add($"[{System.DateTime.Now:HH:mm:ss}] {msg}"); |
| 112 | + if (log.Count > MAX_LOG) log.RemoveAt(0); |
| 113 | + } |
| 114 | + |
| 115 | + // ── 畫面顯示(不需要額外 UI 物件)────────────────────────────────────── |
| 116 | + void OnGUI() |
| 117 | + { |
| 118 | + GUIStyle bg = new GUIStyle(GUI.skin.box) |
| 119 | + { |
| 120 | + fontSize = 14, |
| 121 | + alignment = TextAnchor.UpperLeft, |
| 122 | + richText = true, |
| 123 | + wordWrap = false |
| 124 | + }; |
| 125 | + |
| 126 | + // 設備狀態面板 |
| 127 | + GUILayout.BeginArea(new Rect(10, 10, 380, 200), "設備狀態", GUI.skin.window); |
| 128 | + if (devices.Count == 0) |
| 129 | + { |
| 130 | + GUILayout.Label("尚未收到任何設備資料..."); |
| 131 | + } |
| 132 | + else |
| 133 | + { |
| 134 | + foreach (var d in devices.Values) |
| 135 | + { |
| 136 | + string color = d.IsOnline ? "#55ff55" : "#ff5555"; |
| 137 | + string status = d.IsOnline ? "● 在線" : "○ 離線"; |
| 138 | + GUILayout.Label( |
| 139 | + $"<color={color}>{status}</color> " + |
| 140 | + $"<b>{d.Id}</b> " + |
| 141 | + $"seq={d.Seq} temp={d.Temp}°C humid={d.Humid}%", |
| 142 | + bg); |
| 143 | + } |
| 144 | + } |
| 145 | + GUILayout.EndArea(); |
| 146 | + |
| 147 | + // 事件 log 面板 |
| 148 | + GUILayout.BeginArea(new Rect(10, 220, 500, 230), "事件記錄", GUI.skin.window); |
| 149 | + foreach (var entry in log) |
| 150 | + GUILayout.Label(entry, bg); |
| 151 | + GUILayout.EndArea(); |
| 152 | + } |
| 153 | +} |
0 commit comments