com.xrlab.labframe_brainbit 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/main.yml +27 -0
- package/.github/workflows/main.yml.meta +7 -0
- package/Editor/BrainBitPostProcess.cs +31 -0
- package/Editor/BrainBitPostProcess.cs.meta +11 -0
- package/Editor.meta +8 -0
- package/LICENSE +21 -0
- package/LICENSE.meta +7 -0
- package/README.md +95 -0
- package/README.md.meta +7 -0
- package/Runtime/Plugins/Android/AndroidManifest.xml +15 -0
- package/Runtime/Plugins/Android/AndroidManifest.xml.meta +7 -0
- package/Runtime/Plugins/Android.meta +8 -0
- package/Runtime/Plugins.meta +8 -0
- package/Runtime.meta +8 -0
- package/Scripts/BrainBitData.cs +187 -0
- package/Scripts/BrainBitData.cs.meta +11 -0
- package/Scripts/BrainBitManager.cs +802 -0
- package/Scripts/BrainBitManager.cs.meta +11 -0
- package/Scripts/Brainbit.asmdef +18 -0
- package/Scripts/Brainbit.asmdef.meta +7 -0
- package/Scripts/BrainbitCheckController.cs +669 -0
- package/Scripts/BrainbitCheckController.cs.meta +11 -0
- package/Scripts/Resource/Config/BrainBitConfig.cs +51 -0
- package/Scripts/Resource/Config/BrainBitConfig.cs.meta +11 -0
- package/Scripts/Resource/Config.meta +8 -0
- package/Scripts/Resource/IManagers/BrainBitController.prefab +46 -0
- package/Scripts/Resource/IManagers/BrainBitController.prefab.meta +7 -0
- package/Scripts/Resource/IManagers.meta +8 -0
- package/Scripts/Resource.meta +8 -0
- package/Scripts/Sample/SampleScene.unity +4556 -0
- package/Scripts/Sample/SampleScene.unity.meta +7 -0
- package/Scripts/Sample.meta +8 -0
- package/Scripts.meta +8 -0
- package/package.json +28 -0
- package/package.json.meta +7 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Brainbit",
|
|
3
|
+
"rootNamespace": "",
|
|
4
|
+
"references": [
|
|
5
|
+
"GUID:446fbb70571d4224d98108c0517d6b29",
|
|
6
|
+
"GUID:a5a806c53ddbe413484b9d0109675c29",
|
|
7
|
+
"GUID:6055be8ebefd69e48b49212b09b47b2f"
|
|
8
|
+
],
|
|
9
|
+
"includePlatforms": [],
|
|
10
|
+
"excludePlatforms": [],
|
|
11
|
+
"allowUnsafeCode": false,
|
|
12
|
+
"overrideReferences": false,
|
|
13
|
+
"precompiledReferences": [],
|
|
14
|
+
"autoReferenced": true,
|
|
15
|
+
"defineConstraints": [],
|
|
16
|
+
"versionDefines": [],
|
|
17
|
+
"noEngineReferences": false
|
|
18
|
+
}
|
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
using System.Collections;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using UnityEngine.SceneManagement;
|
|
4
|
+
using UnityEngine;
|
|
5
|
+
using UnityEngine.UI;
|
|
6
|
+
using UnityEngine.Android;
|
|
7
|
+
using System;
|
|
8
|
+
using LabFrame2023;
|
|
9
|
+
using TMPro;
|
|
10
|
+
|
|
11
|
+
/// <summary>
|
|
12
|
+
/// BrainBit 阻抗檢查控制器
|
|
13
|
+
/// 專門用於檢測和顯示 BrainBit 設備的阻抗狀態
|
|
14
|
+
/// 通過 BrainBitManager 進行設備管理和數據採集
|
|
15
|
+
/// </summary>
|
|
16
|
+
public class BrainBitCheckController : MonoBehaviour
|
|
17
|
+
{
|
|
18
|
+
[Header("阻抗檢測 UI")]
|
|
19
|
+
public Image[] impedanceIcon = new Image[4]; // T3, T4, O1, O2 狀態指示器
|
|
20
|
+
public TextMeshProUGUI[] impedanceValues = new TextMeshProUGUI[4]; // 阻抗數值顯示
|
|
21
|
+
public TextMeshProUGUI[] channelLabels = new TextMeshProUGUI[4]; // 通道標籤 (T3, T4, O1, O2)
|
|
22
|
+
|
|
23
|
+
[Header("連接狀態 UI")]
|
|
24
|
+
public Image ConnectStatus; // 連接狀態指示器
|
|
25
|
+
public TextMeshProUGUI ConnectText; // 連接狀態文字
|
|
26
|
+
public TextMeshProUGUI DeviceInfoText; // 設備信息顯示
|
|
27
|
+
|
|
28
|
+
[Header("控制按鈕")]
|
|
29
|
+
public Button MainMenuBtn; // 返回主選單
|
|
30
|
+
public Button ConnectBtn; // 連接設備
|
|
31
|
+
public Button DisconnectBtn; // 斷開連接
|
|
32
|
+
public Button StartImpedanceBtn; // 開始阻抗檢測
|
|
33
|
+
public Button StopImpedanceBtn; // 停止阻抗檢測
|
|
34
|
+
public Button RefreshBtn; // 重新掃描設備
|
|
35
|
+
|
|
36
|
+
[Header("阻抗檢測設定")]
|
|
37
|
+
public Color excellentImpedanceColor = Color.green; // 優秀阻抗顏色
|
|
38
|
+
public Color badImpedanceColor = Color.red; // 不良阻抗顏色
|
|
39
|
+
|
|
40
|
+
[Header("阻抗閾值設定")]
|
|
41
|
+
[Tooltip("阻抗標準,小於等於此值為正常(綠色),大於為不良(紅色)")]
|
|
42
|
+
public double threshold = 200000.0;
|
|
43
|
+
|
|
44
|
+
[Header("檢測設定")]
|
|
45
|
+
public float impedanceUpdateInterval = 0.5f; // 阻抗更新間隔
|
|
46
|
+
public bool autoStartImpedanceOnConnect = true; // 連接後自動開始阻抗檢測
|
|
47
|
+
public bool showImpedanceWarnings = true; // 顯示阻抗警告
|
|
48
|
+
|
|
49
|
+
private BrainBitManager brainBitManager;
|
|
50
|
+
private BrainBitConfig config;
|
|
51
|
+
private Coroutine impedanceCheckCoroutine;
|
|
52
|
+
private bool isImpedanceCheckActive = false;
|
|
53
|
+
private float lastImpedanceUpdateTime = 0f;
|
|
54
|
+
|
|
55
|
+
// 阻抗檢測狀態
|
|
56
|
+
private readonly string[] channelNames = { "T3", "T4", "O1", "O2" };
|
|
57
|
+
private bool[] channelWarningShown = new bool[4];
|
|
58
|
+
private double[] lastImpedanceValues = new double[4];
|
|
59
|
+
|
|
60
|
+
// UI 更新狀態
|
|
61
|
+
private bool needsUIUpdate = true;
|
|
62
|
+
|
|
63
|
+
void Start()
|
|
64
|
+
{
|
|
65
|
+
#if UNITY_ANDROID && !UNITY_EDITOR
|
|
66
|
+
if (!Permission.HasUserAuthorizedPermission(Permission.FineLocation))
|
|
67
|
+
{
|
|
68
|
+
Permission.RequestUserPermission(Permission.FineLocation);
|
|
69
|
+
}
|
|
70
|
+
if (!Permission.HasUserAuthorizedPermission("android.permission.BLUETOOTH_SCAN"))
|
|
71
|
+
{
|
|
72
|
+
Permission.RequestUserPermission("android.permission.BLUETOOTH_SCAN");
|
|
73
|
+
}
|
|
74
|
+
if (!Permission.HasUserAuthorizedPermission("android.permission.BLUETOOTH_CONNECT"))
|
|
75
|
+
{
|
|
76
|
+
Permission.RequestUserPermission("android.permission.BLUETOOTH_CONNECT");
|
|
77
|
+
}
|
|
78
|
+
Debug.Log("FineLocation: " + Permission.HasUserAuthorizedPermission(Permission.FineLocation));
|
|
79
|
+
Debug.Log("BT_SCAN: " + Permission.HasUserAuthorizedPermission("android.permission.BLUETOOTH_SCAN"));
|
|
80
|
+
Debug.Log("BT_CONNECT: " + Permission.HasUserAuthorizedPermission("android.permission.BLUETOOTH_CONNECT"));
|
|
81
|
+
#endif
|
|
82
|
+
InitializeImpedanceChecker();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
void Update()
|
|
86
|
+
{
|
|
87
|
+
UpdateConnectionStatus();
|
|
88
|
+
UpdateImpedanceDisplay();
|
|
89
|
+
|
|
90
|
+
// 定期更新 UI(避免每幀都更新)
|
|
91
|
+
if (needsUIUpdate || Time.time - lastImpedanceUpdateTime > impedanceUpdateInterval)
|
|
92
|
+
{
|
|
93
|
+
UpdateButtonStates();
|
|
94
|
+
needsUIUpdate = false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
#region 初始化方法
|
|
99
|
+
|
|
100
|
+
private void InitializeImpedanceChecker()
|
|
101
|
+
{
|
|
102
|
+
Debug.Log("[BrainBitChecker] 初始化阻抗檢測器...");
|
|
103
|
+
|
|
104
|
+
// 獲取 BrainBitManager 實例
|
|
105
|
+
brainBitManager = BrainBitManager.Instance;
|
|
106
|
+
|
|
107
|
+
// 載入配置
|
|
108
|
+
config = LabTools.GetConfig<BrainBitConfig>();
|
|
109
|
+
|
|
110
|
+
// 初始化 UI
|
|
111
|
+
InitializeUI();
|
|
112
|
+
|
|
113
|
+
// 設置按鈕事件
|
|
114
|
+
SetupButtons();
|
|
115
|
+
|
|
116
|
+
// 訂閱事件
|
|
117
|
+
SubscribeToEvents();
|
|
118
|
+
|
|
119
|
+
Debug.Log("[BrainBitChecker] 阻抗檢測器初始化完成");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private void InitializeUI()
|
|
123
|
+
{
|
|
124
|
+
// 設置通道標籤
|
|
125
|
+
for (int i = 0; i < channelLabels.Length && i < channelNames.Length; i++)
|
|
126
|
+
{
|
|
127
|
+
if (channelLabels[i] != null)
|
|
128
|
+
channelLabels[i].text = channelNames[i];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 初始化阻抗顯示
|
|
132
|
+
ResetImpedanceDisplay();
|
|
133
|
+
|
|
134
|
+
// 初始化連接狀態
|
|
135
|
+
UpdateConnectionStatusDisplay(false, "No Connection");
|
|
136
|
+
|
|
137
|
+
// 設置初始按鈕狀態
|
|
138
|
+
UpdateButtonStates();
|
|
139
|
+
|
|
140
|
+
Debug.Log("[BrainBitChecker] UI 初始化完成");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
#endregion
|
|
144
|
+
|
|
145
|
+
#region UI 更新方法
|
|
146
|
+
|
|
147
|
+
private void UpdateConnectionStatus()
|
|
148
|
+
{
|
|
149
|
+
if (brainBitManager == null) return;
|
|
150
|
+
|
|
151
|
+
bool isConnected = brainBitManager.IsConnected;
|
|
152
|
+
string statusText = GetConnectionStatusText();
|
|
153
|
+
|
|
154
|
+
UpdateConnectionStatusDisplay(isConnected, statusText);
|
|
155
|
+
UpdateDeviceInfo();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private string GetConnectionStatusText()
|
|
159
|
+
{
|
|
160
|
+
if (brainBitManager.IsConnected)
|
|
161
|
+
{
|
|
162
|
+
if (brainBitManager.IsStreamingImpedance)
|
|
163
|
+
return "Connected - Detecting";
|
|
164
|
+
else
|
|
165
|
+
return "Connected";
|
|
166
|
+
}
|
|
167
|
+
else if (brainBitManager.IsScanning)
|
|
168
|
+
{
|
|
169
|
+
return "Scanning...";
|
|
170
|
+
}
|
|
171
|
+
else
|
|
172
|
+
{
|
|
173
|
+
return "No Connection";
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private void UpdateConnectionStatusDisplay(bool isConnected, string statusText)
|
|
178
|
+
{
|
|
179
|
+
if (ConnectStatus != null)
|
|
180
|
+
{
|
|
181
|
+
ConnectStatus.color = isConnected ? excellentImpedanceColor : badImpedanceColor;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (ConnectText != null)
|
|
185
|
+
{
|
|
186
|
+
ConnectText.text = statusText;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private void UpdateDeviceInfo()
|
|
191
|
+
{
|
|
192
|
+
if (DeviceInfoText == null) return;
|
|
193
|
+
|
|
194
|
+
if (brainBitManager.IsConnected)
|
|
195
|
+
{
|
|
196
|
+
DeviceInfoText.text = $"設備: {brainBitManager.ConnectedDeviceName}\n" +
|
|
197
|
+
$"地址: {brainBitManager.ConnectedDeviceAddress}";
|
|
198
|
+
}
|
|
199
|
+
else
|
|
200
|
+
{
|
|
201
|
+
DeviceInfoText.text = "設備: No Connection";
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private void UpdateImpedanceDisplay()
|
|
206
|
+
{
|
|
207
|
+
if (brainBitManager == null || !brainBitManager.IsConnected) return;
|
|
208
|
+
|
|
209
|
+
var impedanceData = brainBitManager.GetLatestImpedanceData();
|
|
210
|
+
if (impedanceData == null) return;
|
|
211
|
+
|
|
212
|
+
var values = impedanceData.ImpedanceValues;
|
|
213
|
+
lastImpedanceUpdateTime = Time.time;
|
|
214
|
+
|
|
215
|
+
// 更新各通道的阻抗顯示
|
|
216
|
+
for (int i = 0; i < impedanceIcon.Length && i < values.Count; i++)
|
|
217
|
+
{
|
|
218
|
+
UpdateChannelImpedance(i, values[i]);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// 檢查是否需要顯示警告
|
|
222
|
+
CheckImpedanceWarnings(impedanceData);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private void UpdateChannelImpedance(int channelIndex, double impedanceValue)
|
|
226
|
+
{
|
|
227
|
+
// 更新數值顯示
|
|
228
|
+
if (impedanceValues[channelIndex] != null)
|
|
229
|
+
{
|
|
230
|
+
impedanceValues[channelIndex].text = FormatImpedanceValue(impedanceValue);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// 更新狀態指示器顏色
|
|
234
|
+
if (impedanceIcon[channelIndex] != null)
|
|
235
|
+
{
|
|
236
|
+
impedanceIcon[channelIndex].color = GetImpedanceColor(impedanceValue);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// 記錄數值
|
|
240
|
+
lastImpedanceValues[channelIndex] = impedanceValue;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private string FormatImpedanceValue(double value)
|
|
244
|
+
{
|
|
245
|
+
if (value >= 1000000)
|
|
246
|
+
return $"{value / 1000000:F1}MΩ";
|
|
247
|
+
else if (value >= 1000)
|
|
248
|
+
return $"{value / 1000:F0}kΩ";
|
|
249
|
+
else
|
|
250
|
+
return $"{value:F0}Ω";
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private Color GetImpedanceColor(double impedanceValue)
|
|
254
|
+
{
|
|
255
|
+
if (impedanceValue <= threshold)
|
|
256
|
+
return excellentImpedanceColor;
|
|
257
|
+
else
|
|
258
|
+
return badImpedanceColor;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private void CheckImpedanceWarnings(BrainBit_ImpedanceData impedanceData)
|
|
262
|
+
{
|
|
263
|
+
if (!showImpedanceWarnings) return;
|
|
264
|
+
|
|
265
|
+
var values = impedanceData.ImpedanceValues;
|
|
266
|
+
|
|
267
|
+
for (int i = 0; i < values.Count && i < channelWarningShown.Length; i++)
|
|
268
|
+
{
|
|
269
|
+
// 如果阻抗值超過閾值(不良)且還沒顯示過警告
|
|
270
|
+
if (values[i] > threshold && !channelWarningShown[i])
|
|
271
|
+
{
|
|
272
|
+
ShowImpedanceWarning(i, values[i]);
|
|
273
|
+
channelWarningShown[i] = true;
|
|
274
|
+
}
|
|
275
|
+
// 如果阻抗值恢復正常,重置警告標記
|
|
276
|
+
else if (values[i] <= threshold && channelWarningShown[i])
|
|
277
|
+
{
|
|
278
|
+
channelWarningShown[i] = false;
|
|
279
|
+
Debug.Log($"[BrainBitChecker] {channelNames[i]} 通道阻抗已恢復正常: {FormatImpedanceValue(values[i])}");
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private void ShowImpedanceWarning(int channelIndex, double impedanceValue)
|
|
285
|
+
{
|
|
286
|
+
string channelName = channelIndex < channelNames.Length ? channelNames[channelIndex] : $"通道{channelIndex}";
|
|
287
|
+
string message = $"{channelName} 通道阻抗過高!\n" +
|
|
288
|
+
$"當前值: {FormatImpedanceValue(impedanceValue)}\n" +
|
|
289
|
+
$"建議值: < {FormatImpedanceValue(threshold)}\n\n" +
|
|
290
|
+
"請檢查電極接觸是否良好";
|
|
291
|
+
|
|
292
|
+
Debug.LogWarning($"[BrainBitChecker] {message}");
|
|
293
|
+
|
|
294
|
+
// 可以選擇顯示提示框
|
|
295
|
+
// LabPromptBox.Show(message);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
private void ResetImpedanceDisplay()
|
|
299
|
+
{
|
|
300
|
+
// 重置阻抗值顯示
|
|
301
|
+
for (int i = 0; i < impedanceValues.Length; i++)
|
|
302
|
+
{
|
|
303
|
+
if (impedanceValues[i] != null)
|
|
304
|
+
impedanceValues[i].text = "---";
|
|
305
|
+
|
|
306
|
+
if (impedanceIcon[i] != null)
|
|
307
|
+
impedanceIcon[i].color = Color.gray;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// 重置警告標記
|
|
311
|
+
for (int i = 0; i < channelWarningShown.Length; i++)
|
|
312
|
+
{
|
|
313
|
+
channelWarningShown[i] = false;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
Debug.Log("[BrainBitChecker] 阻抗顯示已重置");
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
#endregion
|
|
320
|
+
|
|
321
|
+
#region 按鈕設置和狀態更新
|
|
322
|
+
|
|
323
|
+
private void SetupButtons()
|
|
324
|
+
{
|
|
325
|
+
ConnectBtn?.onClick.AddListener(StartConnection);
|
|
326
|
+
DisconnectBtn?.onClick.AddListener(StopConnection);
|
|
327
|
+
StartImpedanceBtn?.onClick.AddListener(StartImpedanceCheck);
|
|
328
|
+
StopImpedanceBtn?.onClick.AddListener(StopImpedanceCheck);
|
|
329
|
+
RefreshBtn?.onClick.AddListener(RefreshConnection);
|
|
330
|
+
|
|
331
|
+
Debug.Log("[BrainBitChecker] 按鈕事件設置完成");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
private void UpdateButtonStates()
|
|
335
|
+
{
|
|
336
|
+
if (brainBitManager == null) return;
|
|
337
|
+
|
|
338
|
+
bool isConnected = brainBitManager.IsConnected;
|
|
339
|
+
bool isScanning = brainBitManager.IsScanning;
|
|
340
|
+
bool isStreamingImpedance = brainBitManager.IsStreamingImpedance;
|
|
341
|
+
|
|
342
|
+
// 連接相關按鈕
|
|
343
|
+
if (ConnectBtn != null)
|
|
344
|
+
ConnectBtn.interactable = !isConnected && !isScanning;
|
|
345
|
+
|
|
346
|
+
if (DisconnectBtn != null)
|
|
347
|
+
DisconnectBtn.interactable = isConnected;
|
|
348
|
+
|
|
349
|
+
if (RefreshBtn != null)
|
|
350
|
+
RefreshBtn.interactable = !isConnected && !isScanning;
|
|
351
|
+
|
|
352
|
+
// 阻抗檢測按鈕
|
|
353
|
+
if (StartImpedanceBtn != null)
|
|
354
|
+
StartImpedanceBtn.interactable = isConnected && !isStreamingImpedance;
|
|
355
|
+
|
|
356
|
+
if (StopImpedanceBtn != null)
|
|
357
|
+
StopImpedanceBtn.interactable = isStreamingImpedance;
|
|
358
|
+
|
|
359
|
+
// 主選單按鈕始終可用
|
|
360
|
+
if (MainMenuBtn != null)
|
|
361
|
+
MainMenuBtn.interactable = true;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
#endregion
|
|
365
|
+
|
|
366
|
+
#region 事件訂閱和處理
|
|
367
|
+
|
|
368
|
+
private void SubscribeToEvents()
|
|
369
|
+
{
|
|
370
|
+
if (brainBitManager != null)
|
|
371
|
+
{
|
|
372
|
+
brainBitManager.OnConnectionStatusChanged += OnConnectionStatusChanged;
|
|
373
|
+
brainBitManager.OnImpedanceDataReceived += OnImpedanceDataReceived;
|
|
374
|
+
brainBitManager.OnDeviceFound += OnDeviceFound;
|
|
375
|
+
brainBitManager.OnError += OnError;
|
|
376
|
+
|
|
377
|
+
Debug.Log("[BrainBitChecker] 事件訂閱完成");
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
private void UnsubscribeFromEvents()
|
|
382
|
+
{
|
|
383
|
+
if (brainBitManager != null)
|
|
384
|
+
{
|
|
385
|
+
brainBitManager.OnConnectionStatusChanged -= OnConnectionStatusChanged;
|
|
386
|
+
brainBitManager.OnImpedanceDataReceived -= OnImpedanceDataReceived;
|
|
387
|
+
brainBitManager.OnDeviceFound -= OnDeviceFound;
|
|
388
|
+
brainBitManager.OnError -= OnError;
|
|
389
|
+
|
|
390
|
+
Debug.Log("[BrainBitChecker] 事件取消訂閱完成");
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
#region 事件處理器
|
|
395
|
+
|
|
396
|
+
private void OnConnectionStatusChanged(bool isConnected)
|
|
397
|
+
{
|
|
398
|
+
needsUIUpdate = true;
|
|
399
|
+
|
|
400
|
+
if (isConnected)
|
|
401
|
+
{
|
|
402
|
+
Debug.Log("[BrainBitChecker] 設備連接成功");
|
|
403
|
+
|
|
404
|
+
// 自動開始阻抗檢測
|
|
405
|
+
if (autoStartImpedanceOnConnect)
|
|
406
|
+
{
|
|
407
|
+
StartCoroutine(DelayedStartImpedance());
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
else
|
|
411
|
+
{
|
|
412
|
+
Debug.Log("[BrainBitChecker] 設備已斷開連接");
|
|
413
|
+
|
|
414
|
+
// 停止阻抗檢測
|
|
415
|
+
StopImpedanceCheck();
|
|
416
|
+
|
|
417
|
+
// 重置顯示
|
|
418
|
+
ResetImpedanceDisplay();
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
private void OnImpedanceDataReceived(BrainBit_ImpedanceData impedanceData)
|
|
423
|
+
{
|
|
424
|
+
// 數據接收處理在 UpdateImpedanceDisplay 中進行
|
|
425
|
+
// 這裡可以添加額外的處理邏輯
|
|
426
|
+
|
|
427
|
+
if (Time.time - lastImpedanceUpdateTime > 5f) // 每5秒打印一次日誌
|
|
428
|
+
{
|
|
429
|
+
Debug.Log($"[BrainBitChecker] 阻抗數據更新: {impedanceData}");
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private void OnDeviceFound(List<NeuroSDK.SensorInfo> devices)
|
|
434
|
+
{
|
|
435
|
+
Debug.Log($"[BrainBitChecker] 發現 {devices.Count} 個設備");
|
|
436
|
+
|
|
437
|
+
foreach (var device in devices)
|
|
438
|
+
{
|
|
439
|
+
Debug.Log($"[BrainBitChecker] 設備: {device.Name} ({device.Address})");
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (devices.Count == 0)
|
|
443
|
+
{
|
|
444
|
+
LabPromptBox.Show("未發現 BrainBit 設備\n請確認設備已開啟且在範圍內");
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
private void OnError(string errorMessage)
|
|
449
|
+
{
|
|
450
|
+
Debug.LogError($"[BrainBitChecker] BrainBit 錯誤: {errorMessage}");
|
|
451
|
+
|
|
452
|
+
// 顯示錯誤提示
|
|
453
|
+
string userMessage = $"BrainBit 設備錯誤:\n{errorMessage}";
|
|
454
|
+
LabPromptBox.Show(userMessage);
|
|
455
|
+
|
|
456
|
+
// 如果是連接錯誤,重置狀態
|
|
457
|
+
if (errorMessage.Contains("connection") || errorMessage.Contains("連接"))
|
|
458
|
+
{
|
|
459
|
+
ResetImpedanceDisplay();
|
|
460
|
+
needsUIUpdate = true;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
#endregion
|
|
465
|
+
|
|
466
|
+
#region 按鈕事件處理器
|
|
467
|
+
|
|
468
|
+
private void StartConnection()
|
|
469
|
+
{
|
|
470
|
+
Debug.Log("[BrainBitChecker] 開始連接設備...");
|
|
471
|
+
brainBitManager?.ManualConnect();
|
|
472
|
+
needsUIUpdate = true;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
private void StopConnection()
|
|
476
|
+
{
|
|
477
|
+
Debug.Log("[BrainBitChecker] 停止連接...");
|
|
478
|
+
|
|
479
|
+
// 先停止阻抗檢測
|
|
480
|
+
StopImpedanceCheck();
|
|
481
|
+
|
|
482
|
+
// 然後斷開連接
|
|
483
|
+
brainBitManager?.ManualDisconnect();
|
|
484
|
+
needsUIUpdate = true;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
private void RefreshConnection()
|
|
488
|
+
{
|
|
489
|
+
Debug.Log("[BrainBitChecker] 重新掃描設備...");
|
|
490
|
+
|
|
491
|
+
// 如果已連接,先斷開
|
|
492
|
+
if (brainBitManager?.IsConnected == true)
|
|
493
|
+
{
|
|
494
|
+
StopConnection();
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// 開始新的掃描
|
|
498
|
+
StartCoroutine(DelayedStartConnection());
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
private void StartImpedanceCheck()
|
|
502
|
+
{
|
|
503
|
+
if (brainBitManager?.IsConnected != true)
|
|
504
|
+
{
|
|
505
|
+
Debug.LogWarning("[BrainBitChecker] 無法開始阻抗檢測 - 設備未連接");
|
|
506
|
+
LabPromptBox.Show("請先連接 BrainBit 設備");
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
Debug.Log("[BrainBitChecker] 開始阻抗檢測...");
|
|
511
|
+
|
|
512
|
+
// 重置警告標記
|
|
513
|
+
for (int i = 0; i < channelWarningShown.Length; i++)
|
|
514
|
+
{
|
|
515
|
+
channelWarningShown[i] = false;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// 開始阻抗數據流(不自動保存到 LabData,因為這只是檢測)
|
|
519
|
+
brainBitManager.StartImpedanceStream(false);
|
|
520
|
+
|
|
521
|
+
isImpedanceCheckActive = true;
|
|
522
|
+
needsUIUpdate = true;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
private void StopImpedanceCheck()
|
|
526
|
+
{
|
|
527
|
+
if (brainBitManager?.IsStreamingImpedance == true)
|
|
528
|
+
{
|
|
529
|
+
Debug.Log("[BrainBitChecker] 停止阻抗檢測...");
|
|
530
|
+
brainBitManager.StopImpedanceStream();
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
isImpedanceCheckActive = false;
|
|
534
|
+
needsUIUpdate = true;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
private void BacktoMainMenu()
|
|
538
|
+
{
|
|
539
|
+
Debug.Log("[BrainBitChecker] 返回主選單...");
|
|
540
|
+
|
|
541
|
+
// 停止所有數據採集
|
|
542
|
+
StopImpedanceCheck();
|
|
543
|
+
|
|
544
|
+
// 載入主選單場景
|
|
545
|
+
SceneManager.LoadScene("MainUI");
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
#endregion
|
|
549
|
+
|
|
550
|
+
#region 協程方法
|
|
551
|
+
|
|
552
|
+
private IEnumerator DelayedStartImpedance()
|
|
553
|
+
{
|
|
554
|
+
// 等待連接穩定
|
|
555
|
+
yield return new WaitForSeconds(1.0f);
|
|
556
|
+
|
|
557
|
+
if (brainBitManager?.IsConnected == true)
|
|
558
|
+
{
|
|
559
|
+
StartImpedanceCheck();
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
private IEnumerator DelayedStartConnection()
|
|
564
|
+
{
|
|
565
|
+
// 等待一下再開始連接
|
|
566
|
+
yield return new WaitForSeconds(0.5f);
|
|
567
|
+
StartConnection();
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
#endregion
|
|
571
|
+
|
|
572
|
+
#region 公共 API(向後相容性)
|
|
573
|
+
|
|
574
|
+
/// <summary>
|
|
575
|
+
/// 獲取指定通道的阻抗數據(向後相容性)
|
|
576
|
+
/// </summary>
|
|
577
|
+
/// <param name="channel">通道索引 (0=T3, 1=T4, 2=O1, 3=O2)</param>
|
|
578
|
+
/// <returns>阻抗值(歐姆)</returns>
|
|
579
|
+
public double GetImpedanceData(int channel)
|
|
580
|
+
{
|
|
581
|
+
if (brainBitManager == null)
|
|
582
|
+
{
|
|
583
|
+
Debug.LogWarning("[BrainBitChecker] BrainBitManager 未初始化");
|
|
584
|
+
return 300000; // 預設高阻抗值
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
var impedanceData = brainBitManager.GetLatestImpedanceData();
|
|
588
|
+
if (impedanceData == null || channel < 0 || channel >= impedanceData.ImpedanceValues.Count)
|
|
589
|
+
{
|
|
590
|
+
Debug.LogWarning($"[BrainBitChecker] 無效的通道索引或無阻抗數據: {channel}");
|
|
591
|
+
return 300000;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return impedanceData.ImpedanceValues[channel];
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/// <summary>
|
|
598
|
+
/// 獲取指定通道的 EEG 數據(向後相容性)
|
|
599
|
+
/// </summary>
|
|
600
|
+
/// <param name="channel">通道索引 (0=T3, 1=T4, 2=O1, 3=O2)</param>
|
|
601
|
+
/// <returns>EEG 值</returns>
|
|
602
|
+
public double GetEegData(int channel)
|
|
603
|
+
{
|
|
604
|
+
if (brainBitManager == null) return 0;
|
|
605
|
+
|
|
606
|
+
var eegData = brainBitManager.GetLatestEEGData();
|
|
607
|
+
if (eegData == null || channel < 0 || channel >= eegData.EEGValues.Count)
|
|
608
|
+
return 0;
|
|
609
|
+
|
|
610
|
+
return eegData.EEGValues[channel];
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/// <summary>
|
|
614
|
+
/// 連接狀態屬性(向後相容性)
|
|
615
|
+
/// </summary>
|
|
616
|
+
public bool connectionStatus => brainBitManager?.IsConnected ?? false;
|
|
617
|
+
|
|
618
|
+
/// <summary>
|
|
619
|
+
/// 開始阻抗測量(向後相容性)
|
|
620
|
+
/// </summary>
|
|
621
|
+
public void StartResistance()
|
|
622
|
+
{
|
|
623
|
+
StartImpedanceCheck();
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/// <summary>
|
|
627
|
+
/// 停止阻抗測量(向後相容性)
|
|
628
|
+
/// </summary>
|
|
629
|
+
public void StopResistance()
|
|
630
|
+
{
|
|
631
|
+
StopImpedanceCheck();
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/// <summary>
|
|
635
|
+
/// 掃描設備(向後相容性)
|
|
636
|
+
/// </summary>
|
|
637
|
+
public void Scanning()
|
|
638
|
+
{
|
|
639
|
+
StartConnection();
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/// <summary>
|
|
643
|
+
/// 獲取阻抗檢測狀態
|
|
644
|
+
/// </summary>
|
|
645
|
+
public bool IsImpedanceCheckActive => isImpedanceCheckActive;
|
|
646
|
+
|
|
647
|
+
/// <summary>
|
|
648
|
+
/// 獲取所有通道的阻抗狀態摘要
|
|
649
|
+
/// </summary>
|
|
650
|
+
public string GetImpedanceStatusSummary()
|
|
651
|
+
{
|
|
652
|
+
if (brainBitManager?.IsConnected != true)
|
|
653
|
+
return "設備未連接";
|
|
654
|
+
|
|
655
|
+
var impedanceData = brainBitManager.GetLatestImpedanceData();
|
|
656
|
+
if (impedanceData == null)
|
|
657
|
+
return "無阻抗數據";
|
|
658
|
+
|
|
659
|
+
return impedanceData.GetImpedanceStatus();
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
#endregion
|
|
663
|
+
|
|
664
|
+
void OnDestroy()
|
|
665
|
+
{
|
|
666
|
+
UnsubscribeFromEvents();
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
#endregion
|