Skip to content

Commit 335a30f

Browse files
Reymerclaude
andcommitted
[新增] Unity TCPListener 模擬情境 Sample
SimulateTCPListener.cs 搭配 EdgeLinkManager TCPListener 模式, 可與 FakeArduino 對接驗證完整流程,含設備狀態面板與事件記錄 OnGUI。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 30c3d16 commit 335a30f

2 files changed

Lines changed: 159 additions & 1 deletion

File tree

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
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+
}

SDK/Unity/Package/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,13 @@
1111
"samples": [
1212
{
1313
"displayName": "Basic Example",
14-
"description": "示範如何使用 EdgeLinkManager 取得感測器資料。",
14+
"description": "示範如何使用 EdgeLinkManager 取得感測器資料、設備斷線偵測",
1515
"path": "Samples~/Basic"
16+
},
17+
{
18+
"displayName": "TCPListener Simulate",
19+
"description": "模擬 TCPListener 情境:Unity 開 Port 等待 EdgeLink 連入,搭配 FakeArduino 驗證完整流程。",
20+
"path": "Samples~/TCPListenerSimulate"
1621
}
1722
]
1823
}

0 commit comments

Comments
 (0)