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.
Files changed (35) hide show
  1. package/.github/workflows/main.yml +27 -0
  2. package/.github/workflows/main.yml.meta +7 -0
  3. package/Editor/BrainBitPostProcess.cs +31 -0
  4. package/Editor/BrainBitPostProcess.cs.meta +11 -0
  5. package/Editor.meta +8 -0
  6. package/LICENSE +21 -0
  7. package/LICENSE.meta +7 -0
  8. package/README.md +95 -0
  9. package/README.md.meta +7 -0
  10. package/Runtime/Plugins/Android/AndroidManifest.xml +15 -0
  11. package/Runtime/Plugins/Android/AndroidManifest.xml.meta +7 -0
  12. package/Runtime/Plugins/Android.meta +8 -0
  13. package/Runtime/Plugins.meta +8 -0
  14. package/Runtime.meta +8 -0
  15. package/Scripts/BrainBitData.cs +187 -0
  16. package/Scripts/BrainBitData.cs.meta +11 -0
  17. package/Scripts/BrainBitManager.cs +802 -0
  18. package/Scripts/BrainBitManager.cs.meta +11 -0
  19. package/Scripts/Brainbit.asmdef +18 -0
  20. package/Scripts/Brainbit.asmdef.meta +7 -0
  21. package/Scripts/BrainbitCheckController.cs +669 -0
  22. package/Scripts/BrainbitCheckController.cs.meta +11 -0
  23. package/Scripts/Resource/Config/BrainBitConfig.cs +51 -0
  24. package/Scripts/Resource/Config/BrainBitConfig.cs.meta +11 -0
  25. package/Scripts/Resource/Config.meta +8 -0
  26. package/Scripts/Resource/IManagers/BrainBitController.prefab +46 -0
  27. package/Scripts/Resource/IManagers/BrainBitController.prefab.meta +7 -0
  28. package/Scripts/Resource/IManagers.meta +8 -0
  29. package/Scripts/Resource.meta +8 -0
  30. package/Scripts/Sample/SampleScene.unity +4556 -0
  31. package/Scripts/Sample/SampleScene.unity.meta +7 -0
  32. package/Scripts/Sample.meta +8 -0
  33. package/Scripts.meta +8 -0
  34. package/package.json +28 -0
  35. package/package.json.meta +7 -0
@@ -0,0 +1,802 @@
1
+ using UnityEngine;
2
+ using System.Collections;
3
+ using System.Collections.Generic;
4
+ using LabFrame2023;
5
+ using UnityEngine.Events;
6
+ using NeuroSDK;
7
+ using System;
8
+
9
+ /// <summary>
10
+ /// BrainBit 設備管理器
11
+ /// 負責管理 BrainBit 設備的連接、數據採集和記錄
12
+ /// </summary>
13
+ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
14
+ {
15
+ #region Properties
16
+ /// <summary>
17
+ /// 目前是否有與 BrainBit 設備連線
18
+ /// </summary>
19
+ public bool IsConnected { get; private set; } = false;
20
+
21
+ /// <summary>
22
+ /// 正在記錄 EEG 數據?
23
+ /// </summary>
24
+ public bool IsStreamingEEG { get; private set; } = false;
25
+
26
+ /// <summary>
27
+ /// 正在記錄阻抗數據?
28
+ /// </summary>
29
+ public bool IsStreamingImpedance { get; private set; } = false;
30
+
31
+ /// <summary>
32
+ /// 目前連接的設備名稱
33
+ /// </summary>
34
+ public string ConnectedDeviceName { get; private set; } = "";
35
+
36
+ /// <summary>
37
+ /// 目前連接的設備地址
38
+ /// </summary>
39
+ public string ConnectedDeviceAddress { get; private set; } = "";
40
+
41
+ /// <summary>
42
+ /// 是否正在掃描設備
43
+ /// </summary>
44
+ public bool IsScanning { get; private set; } = false;
45
+ #endregion
46
+
47
+ #region Events
48
+ /// <summary>
49
+ /// EEG 數據更新事件
50
+ /// </summary>
51
+ public event UnityAction<BrainBit_EEGData> OnEEGDataReceived;
52
+
53
+ /// <summary>
54
+ /// 阻抗數據更新事件
55
+ /// </summary>
56
+ public event UnityAction<BrainBit_ImpedanceData> OnImpedanceDataReceived;
57
+
58
+ /// <summary>
59
+ /// 連接狀態變化事件
60
+ /// </summary>
61
+ public event UnityAction<bool> OnConnectionStatusChanged;
62
+
63
+ /// <summary>
64
+ /// 設備掃描完成事件
65
+ /// </summary>
66
+ public event UnityAction<List<SensorInfo>> OnDeviceFound;
67
+
68
+ /// <summary>
69
+ /// 錯誤事件
70
+ /// </summary>
71
+ public event UnityAction<string> OnError;
72
+ #endregion
73
+
74
+ #region Private Fields
75
+ private BrainBitConfig _config;
76
+ private Scanner _scanner;
77
+ private BrainBitSensor _currentSensor;
78
+
79
+ private BrainBit_EEGData _lastEEGData;
80
+ private BrainBit_ImpedanceData _lastImpedanceData;
81
+
82
+ private bool _autoWriteEEGData = false;
83
+ private bool _autoWriteImpedanceData = false;
84
+
85
+ private Coroutine _connectionMonitorCoroutine;
86
+ private Coroutine _scanTimeoutCoroutine;
87
+
88
+ private int _reconnectAttempts = 0;
89
+
90
+ // 用於記錄目前 EEG 寫入資料的標籤 (對應不同的儲存檔案)
91
+ private string _currentEEGTag = "eeg";
92
+ // 用於記錄目前阻抗寫入資料的標籤
93
+ private string _currentImpedanceTag = "impedance";
94
+ #endregion
95
+
96
+ #region IManager Implementation
97
+ public void ManagerInit()
98
+ {
99
+ LabTools.Log("[BrainBit] Initializing BrainBit Manager...");
100
+
101
+ // 載入配置
102
+ _config = LabTools.GetConfig<BrainBitConfig>(true);
103
+
104
+ // 初始化數據對象
105
+ _lastEEGData = new BrainBit_EEGData();
106
+ _lastImpedanceData = new BrainBit_ImpedanceData();
107
+
108
+ // 開始連接監控
109
+ _connectionMonitorCoroutine = StartCoroutine(MonitorConnection());
110
+
111
+ // 自動連接
112
+ if (_config.AutoConnectOnInit)
113
+ {
114
+ StartCoroutine(DelayedAutoConnect());
115
+ }
116
+
117
+ LabTools.Log("[BrainBit] Manager initialized successfully");
118
+ LabTools.Log($"[BrainBit] Config - AutoConnect: {_config.AutoConnectOnInit}, ScanTimeout: {_config.ScanTimeoutSeconds}s");
119
+ LabTools.Log($"[BrainBit] Config - AutoSelectBestSignal: {_config.AutoSelectBestSignal}, ConnectDelay: {_config.ConnectDelaySeconds}s");
120
+ }
121
+
122
+ public IEnumerator ManagerDispose()
123
+ {
124
+ LabTools.Log("[BrainBit] Disposing BrainBit Manager...");
125
+
126
+ // 停止所有數據流
127
+ StopEEGStream();
128
+ StopImpedanceStream();
129
+
130
+ // 停止監控協程
131
+ if (_connectionMonitorCoroutine != null)
132
+ {
133
+ StopCoroutine(_connectionMonitorCoroutine);
134
+ _connectionMonitorCoroutine = null;
135
+ }
136
+
137
+ if (_scanTimeoutCoroutine != null)
138
+ {
139
+ StopCoroutine(_scanTimeoutCoroutine);
140
+ _scanTimeoutCoroutine = null;
141
+ }
142
+
143
+ // 斷開連接
144
+ try
145
+ {
146
+ Disconnect();
147
+ }
148
+ catch (Exception e)
149
+ {
150
+ LabTools.LogError($"[BrainBit] Error during disconnect: {e.Message}");
151
+ }
152
+
153
+ // 清理資源
154
+ CleanupResources();
155
+
156
+ LabTools.Log("[BrainBit] Manager disposed successfully");
157
+ yield return null;
158
+ }
159
+ #endregion
160
+
161
+ #region Public Methods
162
+ /// <summary>
163
+ /// 開始掃描 BrainBit 設備
164
+ /// </summary>
165
+ public void StartScan()
166
+ {
167
+ if (IsScanning)
168
+ {
169
+ LabTools.LogError("[BrainBit] Already scanning for devices");
170
+ return;
171
+ }
172
+
173
+ try
174
+ {
175
+ LabTools.Log("[BrainBit] Starting device scan...");
176
+
177
+ // 根據 BrainBit SDK2 文檔,確保使用正確的 SensorFamily
178
+ _scanner = new Scanner(SensorFamily.SensorLEBrainBit);
179
+ _scanner.EventSensorsChanged += OnSensorsFound;
180
+
181
+ // 開始掃描
182
+ _scanner.Start();
183
+
184
+ IsScanning = true;
185
+ LabTools.Log("[BrainBit] Scanner started successfully");
186
+
187
+ // 設置掃描超時
188
+ _scanTimeoutCoroutine = StartCoroutine(ScanTimeout());
189
+
190
+ // 增加調試信息
191
+ StartCoroutine(DebugScanStatus());
192
+ }
193
+ catch (Exception e)
194
+ {
195
+ LabTools.LogError($"[BrainBit] Failed to start scan: {e.Message}");
196
+ OnError?.Invoke($"Scan failed: {e.Message}");
197
+ IsScanning = false;
198
+ }
199
+ }
200
+
201
+ /// <summary>
202
+ /// 調試掃描狀態
203
+ /// </summary>
204
+ private IEnumerator DebugScanStatus()
205
+ {
206
+ int attempts = 0;
207
+ while (IsScanning && attempts < 10)
208
+ {
209
+ yield return new WaitForSeconds(1f);
210
+ attempts++;
211
+
212
+ try
213
+ {
214
+ // 嘗試獲取已發現的設備列表
215
+ var foundDevices = _scanner?.Sensors;
216
+ LabTools.Log($"[BrainBit] Scan attempt {attempts}: Found {foundDevices?.Count ?? 0} devices");
217
+
218
+ if (foundDevices != null && foundDevices.Count > 0)
219
+ {
220
+ foreach (var device in foundDevices)
221
+ {
222
+ LabTools.Log($"[BrainBit] Found device: {device.Name} ({device.Address}) RSSI: {device.RSSI}");
223
+ }
224
+ }
225
+ }
226
+ catch (Exception e)
227
+ {
228
+ LabTools.LogError($"[BrainBit] Error during scan debug: {e.Message}");
229
+ }
230
+ }
231
+ }
232
+
233
+ /// <summary>
234
+ /// 停止掃描
235
+ /// </summary>
236
+ public void StopScan()
237
+ {
238
+ if (!IsScanning) return;
239
+
240
+ try
241
+ {
242
+ _scanner?.Stop();
243
+ IsScanning = false;
244
+
245
+ if (_scanTimeoutCoroutine != null)
246
+ {
247
+ StopCoroutine(_scanTimeoutCoroutine);
248
+ _scanTimeoutCoroutine = null;
249
+ LabTools.Log("[BrainBit] Scan timeout coroutine stopped");
250
+ }
251
+
252
+ LabTools.Log("[BrainBit] Device scan stopped");
253
+ }
254
+ catch (Exception e)
255
+ {
256
+ LabTools.LogError($"[BrainBit] Error stopping scan: {e.Message}");
257
+ }
258
+ }
259
+
260
+ /// <summary>
261
+ /// 手動連接設備
262
+ /// </summary>
263
+ public void ManualConnect()
264
+ {
265
+ if (IsConnected)
266
+ {
267
+ LabTools.LogError("[BrainBit] Already connected to a device");
268
+ return;
269
+ }
270
+
271
+ StartScan();
272
+ }
273
+
274
+ /// <summary>
275
+ /// 手動斷開連接
276
+ /// </summary>
277
+ public void ManualDisconnect()
278
+ {
279
+ Disconnect();
280
+ }
281
+
282
+ /// <summary>
283
+ /// 開始 EEG 數據流
284
+ /// </summary>
285
+ /// <param name="autoWriteToLabData">是否自動保存到 LabDataManager</param>
286
+ /// <param name="tag">寫入資料的標籤(例如可傳入遊戲階段名稱)</param>
287
+ public void StartEEGStream(bool autoWriteToLabData = true, string tag = "eeg")
288
+ {
289
+ if (!IsConnected)
290
+ {
291
+ LabTools.LogError("[BrainBit] Device not connected");
292
+ OnError?.Invoke("Device not connected");
293
+ return;
294
+ }
295
+
296
+ if (IsStreamingEEG)
297
+ {
298
+ LabTools.LogError("[BrainBit] EEG stream already active");
299
+ return;
300
+ }
301
+
302
+ try
303
+ {
304
+ _autoWriteEEGData = autoWriteToLabData;
305
+ _currentEEGTag = string.IsNullOrEmpty(tag) ? "eeg" : tag;
306
+
307
+ _currentSensor.EventBrainBitSignalDataRecived += OnSignalDataReceived;
308
+ _currentSensor.ExecCommand(SensorCommand.CommandStartSignal);
309
+
310
+ IsStreamingEEG = true;
311
+ LabTools.Log($"[BrainBit] EEG stream started with tag: {_currentEEGTag}");
312
+
313
+ // 記錄開始事件
314
+ if (LabDataManager.Instance.IsInited)
315
+ {
316
+ var connectionData = new BrainBit_ConnectionData(true, ConnectedDeviceName, ConnectedDeviceAddress, "EEG_Stream_Started");
317
+ LabDataManager.Instance.WriteData(connectionData, "defaultPhase");
318
+ }
319
+ }
320
+ catch (Exception e)
321
+ {
322
+ LabTools.LogError($"[BrainBit] Failed to start EEG stream: {e.Message}");
323
+ OnError?.Invoke($"EEG stream failed: {e.Message}");
324
+ }
325
+ }
326
+
327
+ /// <summary>
328
+ /// 停止 EEG 數據流
329
+ /// </summary>
330
+ public void StopEEGStream()
331
+ {
332
+ if (!IsStreamingEEG) return;
333
+
334
+ try
335
+ {
336
+ _currentSensor?.ExecCommand(SensorCommand.CommandStopSignal);
337
+ _currentSensor.EventBrainBitSignalDataRecived -= OnSignalDataReceived;
338
+
339
+ IsStreamingEEG = false;
340
+ _autoWriteEEGData = false;
341
+
342
+ LabTools.Log("[BrainBit] EEG stream stopped");
343
+
344
+ // 記錄停止事件
345
+ if (LabDataManager.Instance.IsInited)
346
+ {
347
+ var connectionData = new BrainBit_ConnectionData(true, ConnectedDeviceName, ConnectedDeviceAddress, "EEG_Stream_Stopped");
348
+ LabDataManager.Instance.WriteData(connectionData, "defaultPhase");
349
+ }
350
+ }
351
+ catch (Exception e)
352
+ {
353
+ LabTools.LogError($"[BrainBit] Error stopping EEG stream: {e.Message}");
354
+ }
355
+ }
356
+
357
+ /// <summary>
358
+ /// 開始阻抗數據流
359
+ /// </summary>
360
+ /// <param name="autoWriteToLabData">是否自動保存到 LabDataManager</param>
361
+ /// <param name="tag">寫入資料的標籤(例如可傳入遊戲階段名稱)</param>
362
+ public void StartImpedanceStream(bool autoWriteToLabData = true, string tag = "impedance")
363
+ {
364
+ if (!IsConnected)
365
+ {
366
+ LabTools.LogError("[BrainBit] Device not connected");
367
+ OnError?.Invoke("Device not connected");
368
+ return;
369
+ }
370
+
371
+ if (IsStreamingImpedance)
372
+ {
373
+ LabTools.LogError("[BrainBit] Impedance stream already active");
374
+ return;
375
+ }
376
+
377
+ try
378
+ {
379
+ _autoWriteImpedanceData = autoWriteToLabData;
380
+ _currentImpedanceTag = string.IsNullOrEmpty(tag) ? "impedance" : tag;
381
+
382
+ _currentSensor.EventBrainBitResistDataRecived += OnResistanceDataReceived;
383
+ _currentSensor.ExecCommand(SensorCommand.CommandStartResist);
384
+
385
+ IsStreamingImpedance = true;
386
+ LabTools.Log($"[BrainBit] Impedance stream started with tag: {_currentImpedanceTag}");
387
+
388
+ // 記錄開始事件
389
+ if (LabDataManager.Instance.IsInited)
390
+ {
391
+ var connectionData = new BrainBit_ConnectionData(true, ConnectedDeviceName, ConnectedDeviceAddress, "Impedance_Stream_Started");
392
+ LabDataManager.Instance.WriteData(connectionData, "defaultPhase");
393
+ }
394
+ }
395
+ catch (Exception e)
396
+ {
397
+ LabTools.LogError($"[BrainBit] Failed to start impedance stream: {e.Message}");
398
+ OnError?.Invoke($"Impedance stream failed: {e.Message}");
399
+ }
400
+ }
401
+
402
+ /// <summary>
403
+ /// 停止阻抗數據流
404
+ /// </summary>
405
+ public void StopImpedanceStream()
406
+ {
407
+ if (!IsStreamingImpedance) return;
408
+
409
+ try
410
+ {
411
+ _currentSensor?.ExecCommand(SensorCommand.CommandStopResist);
412
+ _currentSensor.EventBrainBitResistDataRecived -= OnResistanceDataReceived;
413
+
414
+ IsStreamingImpedance = false;
415
+ _autoWriteImpedanceData = false;
416
+
417
+ LabTools.Log("[BrainBit] Impedance stream stopped");
418
+
419
+ // 記錄停止事件
420
+ if (LabDataManager.Instance.IsInited)
421
+ {
422
+ var connectionData = new BrainBit_ConnectionData(true, ConnectedDeviceName, ConnectedDeviceAddress, "Impedance_Stream_Stopped");
423
+ LabDataManager.Instance.WriteData(connectionData, "connection");
424
+ }
425
+ }
426
+ catch (Exception e)
427
+ {
428
+ LabTools.LogError($"[BrainBit] Error stopping impedance stream: {e.Message}");
429
+ }
430
+ }
431
+
432
+ /// <summary>
433
+ /// 獲取最新的 EEG 數據
434
+ /// </summary>
435
+ public BrainBit_EEGData GetLatestEEGData()
436
+ {
437
+ return _lastEEGData;
438
+ }
439
+
440
+ /// <summary>
441
+ /// 動態更改 EEG 寫入資料的 Tag(例如在不停止數據流的情況下,切換遊戲階段)
442
+ /// </summary>
443
+ /// <param name="tag">新的標籤名稱</param>
444
+ public void SetEEGTag(string tag)
445
+ {
446
+ _currentEEGTag = string.IsNullOrEmpty(tag) ? "eeg" : tag;
447
+ LabTools.Log($"[BrainBit] EEG data tag dynamically changed to: {_currentEEGTag}");
448
+ }
449
+
450
+ /// <summary>
451
+ /// 獲取最新的阻抗數據
452
+ /// </summary>
453
+ public BrainBit_ImpedanceData GetLatestImpedanceData()
454
+ {
455
+ return _lastImpedanceData;
456
+ }
457
+
458
+ /// <summary>
459
+ /// 動態更改阻抗寫入資料的 Tag
460
+ /// </summary>
461
+ /// <param name="tag">新的標籤名稱</param>
462
+ public void SetImpedanceTag(string tag)
463
+ {
464
+ _currentImpedanceTag = string.IsNullOrEmpty(tag) ? "impedance" : tag;
465
+ LabTools.Log($"[BrainBit] Impedance data tag dynamically changed to: {_currentImpedanceTag}");
466
+ }
467
+ #endregion
468
+
469
+ #region Private Methods
470
+ private IEnumerator DelayedAutoConnect()
471
+ {
472
+ yield return new WaitForSeconds(1.0f);
473
+ ManualConnect();
474
+ }
475
+
476
+ private IEnumerator MonitorConnection()
477
+ {
478
+ bool lastConnectionStatus = false;
479
+
480
+ while (true)
481
+ {
482
+ // 檢查連接狀態
483
+ bool currentStatus = _currentSensor != null && _currentSensor.State == SensorState.StateInRange;
484
+
485
+ if (currentStatus != lastConnectionStatus)
486
+ {
487
+ IsConnected = currentStatus;
488
+ OnConnectionStatusChanged?.Invoke(IsConnected);
489
+
490
+ if (!IsConnected && lastConnectionStatus)
491
+ {
492
+ // 斷線處理
493
+ HandleDisconnection();
494
+ }
495
+
496
+ lastConnectionStatus = currentStatus;
497
+ }
498
+
499
+ yield return new WaitForSeconds(0.5f);
500
+ }
501
+ }
502
+
503
+ private IEnumerator ScanTimeout()
504
+ {
505
+ LabTools.Log($"[BrainBit] Scan timeout set for {_config.ScanTimeoutSeconds} seconds");
506
+ yield return new WaitForSeconds(_config.ScanTimeoutSeconds);
507
+
508
+ if (_scanTimeoutCoroutine != null && _currentSensor == null)
509
+ {
510
+ StopScan();
511
+ LabTools.LogError("[BrainBit] Scan timeout - no devices found");
512
+ OnError?.Invoke("Scan timeout - no devices found");
513
+ }
514
+ else
515
+ {
516
+ LabTools.Log("[BrainBit] Scan completed before timeout");
517
+ }
518
+ _scanTimeoutCoroutine = null;
519
+ }
520
+
521
+ private void OnSensorsFound(IScanner scanner, IReadOnlyList<SensorInfo> sensors)
522
+ {
523
+ try
524
+ {
525
+ LabTools.Log($"[BrainBit] Found {sensors.Count} device(s)");
526
+
527
+ // 觸發設備發現事件
528
+ OnDeviceFound?.Invoke(new List<SensorInfo>(sensors));
529
+
530
+ if (sensors.Count > 0)
531
+ {
532
+ // 重要:根據 BrainBit SDK 要求,必須先停止掃描才能建立連接
533
+ StopScan();
534
+
535
+ // 選擇要連接的設備
536
+ SensorInfo targetSensor = SelectBestDevice(sensors);
537
+
538
+ LabTools.Log($"[BrainBit] Selected device: {targetSensor.Name} (RSSI: {targetSensor.RSSI})");
539
+ if (_scanTimeoutCoroutine != null)
540
+ {
541
+ StopCoroutine(_scanTimeoutCoroutine);
542
+ _scanTimeoutCoroutine = null;
543
+ LabTools.Log("[BrainBit] Scan timeout coroutine stopped");
544
+ }
545
+
546
+ // 等待一段時間後建立連接(確保掃描完全停止)
547
+ StartCoroutine(DelayedConnect(targetSensor));
548
+ }
549
+ }
550
+ catch (Exception e)
551
+ {
552
+ LabTools.LogError($"[BrainBit] Error in OnSensorsFound: {e.Message}");
553
+ OnError?.Invoke($"Device discovery error: {e.Message}");
554
+ }
555
+ }
556
+
557
+ /// <summary>
558
+ /// 選擇最佳設備
559
+ /// </summary>
560
+ /// <param name="sensors">發現的設備列表</param>
561
+ /// <returns>選中的設備</returns>
562
+ private SensorInfo SelectBestDevice(IReadOnlyList<SensorInfo> sensors)
563
+ {
564
+ if (sensors.Count == 1)
565
+ {
566
+ return sensors[0];
567
+ }
568
+
569
+ // 如果配置為自動選擇信號最強的設備
570
+ if (_config.AutoSelectBestSignal)
571
+ {
572
+ SensorInfo bestDevice = sensors[0];
573
+ foreach (var sensor in sensors)
574
+ {
575
+ LabTools.Log($"[BrainBit] Device: {sensor.Name}, RSSI: {sensor.RSSI} dBm");
576
+
577
+ // RSSI 值越高(越接近 0)信號越強
578
+ if (sensor.RSSI > bestDevice.RSSI)
579
+ {
580
+ bestDevice = sensor;
581
+ }
582
+ }
583
+
584
+ LabTools.Log($"[BrainBit] Best signal device: {bestDevice.Name} (RSSI: {bestDevice.RSSI} dBm)");
585
+ return bestDevice;
586
+ }
587
+ else
588
+ {
589
+ // 選擇第一個設備
590
+ return sensors[0];
591
+ }
592
+ }
593
+
594
+ /// <summary>
595
+ /// 延遲連接設備
596
+ /// </summary>
597
+ /// <param name="sensorInfo">設備信息</param>
598
+ private IEnumerator DelayedConnect(SensorInfo sensorInfo)
599
+ {
600
+ LabTools.Log($"[BrainBit] Waiting {_config.ConnectDelaySeconds}s before connecting...");
601
+
602
+ // 等待指定時間,確保掃描完全停止
603
+ yield return new WaitForSeconds(_config.ConnectDelaySeconds);
604
+
605
+ // 建立連接
606
+ ConnectToDevice(sensorInfo);
607
+ }
608
+
609
+ private void ConnectToDevice(SensorInfo sensorInfo)
610
+ {
611
+ try
612
+ {
613
+ LabTools.Log($"[BrainBit] Connecting to device: {sensorInfo.Name} ({sensorInfo.Address})");
614
+
615
+ // 創建 Sensor(此時掃描已停止)
616
+ _currentSensor = _scanner.CreateSensor(sensorInfo) as BrainBitSensor;
617
+
618
+ if (_currentSensor != null)
619
+ {
620
+ LabTools.Log("[BrainBit] Sensor created successfully, attempting connection...");
621
+
622
+ // 連接設備
623
+ _currentSensor.Connect();
624
+
625
+ // 設置連接信息
626
+ ConnectedDeviceName = sensorInfo.Name;
627
+ ConnectedDeviceAddress = sensorInfo.Address;
628
+
629
+ LabTools.Log($"[BrainBit] Successfully connected to {ConnectedDeviceName}");
630
+
631
+ // 記錄連接事件
632
+ if (LabDataManager.Instance.IsInited)
633
+ {
634
+ var connectionData = new BrainBit_ConnectionData(true, ConnectedDeviceName, ConnectedDeviceAddress, "Connected");
635
+ LabDataManager.Instance.WriteData(connectionData, "connection");
636
+ }
637
+
638
+ _reconnectAttempts = 0;
639
+ }
640
+ else
641
+ {
642
+ LabTools.LogError("[BrainBit] Failed to create sensor object - CreateSensor returned null");
643
+ OnError?.Invoke("Failed to create sensor object");
644
+ }
645
+ }
646
+ catch (Exception e)
647
+ {
648
+ LabTools.LogError($"[BrainBit] Connection failed: {e.Message}");
649
+ OnError?.Invoke($"Connection failed: {e.Message}");
650
+ }
651
+ }
652
+
653
+ private void Disconnect()
654
+ {
655
+ try
656
+ {
657
+ if (IsConnected)
658
+ {
659
+ // 停止所有數據流
660
+ StopEEGStream();
661
+ StopImpedanceStream();
662
+
663
+ // 記錄斷開事件
664
+ if (LabDataManager.Instance.IsInited)
665
+ {
666
+ var connectionData = new BrainBit_ConnectionData(false, ConnectedDeviceName, ConnectedDeviceAddress, "Disconnected");
667
+ LabDataManager.Instance.WriteData(connectionData, "connection");
668
+ }
669
+
670
+ LabTools.Log($"[BrainBit] Disconnected from {ConnectedDeviceName}");
671
+ }
672
+
673
+ _currentSensor = null;
674
+ IsConnected = false;
675
+ ConnectedDeviceName = "";
676
+ ConnectedDeviceAddress = "";
677
+
678
+ OnConnectionStatusChanged?.Invoke(false);
679
+ }
680
+ catch (Exception e)
681
+ {
682
+ LabTools.LogError($"[BrainBit] Error during disconnect: {e.Message}");
683
+ }
684
+ }
685
+
686
+ private void HandleDisconnection()
687
+ {
688
+ LabTools.LogError($"[BrainBit] Device disconnected: {ConnectedDeviceName}");
689
+
690
+ if (_config.DisconnectNotification)
691
+ {
692
+ LabPromptBox.Show($"BrainBit 設備已斷線!\nDevice {ConnectedDeviceName} disconnected!");
693
+ }
694
+
695
+ // 嘗試重連
696
+ if (_config.AutoReconnectAttempts > 0 && _reconnectAttempts < _config.AutoReconnectAttempts)
697
+ {
698
+ StartCoroutine(AttemptReconnect());
699
+ }
700
+
701
+ Disconnect();
702
+ }
703
+
704
+ private IEnumerator AttemptReconnect()
705
+ {
706
+ _reconnectAttempts++;
707
+ LabTools.Log($"[BrainBit] Attempting reconnection ({_reconnectAttempts}/{_config.AutoReconnectAttempts})...");
708
+
709
+ yield return new WaitForSeconds(_config.ReconnectIntervalSeconds);
710
+
711
+ if (!IsConnected)
712
+ {
713
+ StartScan();
714
+ }
715
+ }
716
+
717
+ private void OnSignalDataReceived(ISensor sensor, BrainBitSignalData[] data)
718
+ {
719
+ try
720
+ {
721
+ foreach (var packet in data)
722
+ {
723
+ _lastEEGData = new BrainBit_EEGData(packet.T3, packet.T4, packet.O1, packet.O2);
724
+
725
+ // 觸發事件
726
+ OnEEGDataReceived?.Invoke(_lastEEGData);
727
+
728
+ // 自動保存到 LabDataManager
729
+ if (_autoWriteEEGData && LabDataManager.Instance.IsInited)
730
+ {
731
+ LabDataManager.Instance.WriteData(_lastEEGData, _currentEEGTag);
732
+ }
733
+ }
734
+ }
735
+ catch (Exception e)
736
+ {
737
+ LabTools.LogError($"[BrainBit] Error processing EEG data: {e.Message}");
738
+ }
739
+ }
740
+
741
+ private void OnResistanceDataReceived(ISensor sensor, BrainBitResistData data)
742
+ {
743
+ try
744
+ {
745
+ _lastImpedanceData = new BrainBit_ImpedanceData(data.T3, data.T4, data.O1, data.O2);
746
+
747
+ // 觸發事件
748
+ OnImpedanceDataReceived?.Invoke(_lastImpedanceData);
749
+
750
+ // 檢查阻抗警告: 當有任一通道阻抗超過 200,000 時
751
+ if (!_lastImpedanceData.IsImpedanceGood)
752
+ {
753
+ LabTools.LogError($"[BrainBit] High impedance detected: {_lastImpedanceData.GetImpedanceStatus()}");
754
+ }
755
+
756
+ // 自動保存到 LabDataManager
757
+ if (_autoWriteImpedanceData && LabDataManager.Instance.IsInited)
758
+ {
759
+ LabDataManager.Instance.WriteData(_lastImpedanceData, _currentImpedanceTag);
760
+ }
761
+ }
762
+ catch (Exception e)
763
+ {
764
+ LabTools.LogError($"[BrainBit] Error processing impedance data: {e.Message}");
765
+ }
766
+ }
767
+
768
+ private void CleanupResources()
769
+ {
770
+ try
771
+ {
772
+ // 清理掃描器
773
+ if (_scanner != null)
774
+ {
775
+ _scanner.EventSensorsChanged -= OnSensorsFound;
776
+ _scanner.Stop();
777
+ _scanner = null;
778
+ }
779
+
780
+ // 清理傳感器
781
+ if (_currentSensor != null)
782
+ {
783
+ _currentSensor.EventBrainBitSignalDataRecived -= OnSignalDataReceived;
784
+ _currentSensor.EventBrainBitResistDataRecived -= OnResistanceDataReceived;
785
+ _currentSensor = null;
786
+ }
787
+
788
+ // 重置狀態
789
+ IsConnected = false;
790
+ IsStreamingEEG = false;
791
+ IsStreamingImpedance = false;
792
+ IsScanning = false;
793
+
794
+ LabTools.Log("[BrainBit] Resources cleaned up");
795
+ }
796
+ catch (Exception e)
797
+ {
798
+ LabTools.LogError($"[BrainBit] Error during cleanup: {e.Message}");
799
+ }
800
+ }
801
+ #endregion
802
+ }