com.xrlab.labframe_brainbit 1.1.0 → 1.1.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/Scripts/BrainBitManager.cs +157 -79
- package/package.json +1 -1
|
@@ -124,10 +124,12 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
124
124
|
private bool _autoWriteEEGData = false;
|
|
125
125
|
private bool _autoWriteImpedanceData = false;
|
|
126
126
|
|
|
127
|
-
private Coroutine _connectionMonitorCoroutine;
|
|
128
|
-
private Coroutine _scanTimeoutCoroutine;
|
|
129
|
-
|
|
130
|
-
private int _reconnectAttempts = 0;
|
|
127
|
+
private Coroutine _connectionMonitorCoroutine;
|
|
128
|
+
private Coroutine _scanTimeoutCoroutine;
|
|
129
|
+
|
|
130
|
+
private int _reconnectAttempts = 0;
|
|
131
|
+
private bool _isInitialized = false;
|
|
132
|
+
private bool _autoConnectScheduled = false;
|
|
131
133
|
|
|
132
134
|
// 用於記錄目前 EEG 寫入資料的標籤 (對應不同的儲存檔案)
|
|
133
135
|
private string _currentEEGTag = "eeg";
|
|
@@ -139,10 +141,10 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
139
141
|
|
|
140
142
|
// === Emotions ===
|
|
141
143
|
private EmotionsController _emotionsController;
|
|
142
|
-
private bool
|
|
143
|
-
private string _currentMindTag
|
|
144
|
-
private string _currentSpectralTag
|
|
145
|
-
private BrainBit_MindData
|
|
144
|
+
private bool _autoWriteEmotionData = false;
|
|
145
|
+
private string _currentMindTag = "mind";
|
|
146
|
+
private string _currentSpectralTag = "spectral";
|
|
147
|
+
private BrainBit_MindData _lastMindData;
|
|
146
148
|
private BrainBit_SpectralData _lastSpectralData;
|
|
147
149
|
// 若為 true,代表 EEG 串流是被情緒處理自動啟動的 — StopEmotionsProcessing 時要一起停
|
|
148
150
|
private bool _emotionsStartedEEG = false;
|
|
@@ -154,20 +156,21 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
154
156
|
LabTools.Log("[BrainBit] Initializing BrainBit Manager...");
|
|
155
157
|
|
|
156
158
|
// 載入配置
|
|
157
|
-
|
|
159
|
+
if (!EnsureInitialized(nameof(ManagerInit)))
|
|
160
|
+
{
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
158
163
|
|
|
159
164
|
// 初始化數據對象
|
|
160
|
-
_lastEEGData = new BrainBit_EEGData();
|
|
161
|
-
_lastImpedanceData = new BrainBit_ImpedanceData();
|
|
162
165
|
|
|
163
166
|
// 開始連接監控
|
|
164
|
-
_connectionMonitorCoroutine = StartCoroutine(MonitorConnection());
|
|
165
167
|
|
|
166
168
|
// 自動連接
|
|
167
|
-
if (_config.AutoConnectOnInit)
|
|
168
|
-
{
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
if (_config.AutoConnectOnInit && !_autoConnectScheduled && !IsConnected && !IsScanning)
|
|
170
|
+
{
|
|
171
|
+
_autoConnectScheduled = true;
|
|
172
|
+
StartCoroutine(DelayedAutoConnect());
|
|
173
|
+
}
|
|
171
174
|
|
|
172
175
|
LabTools.Log("[BrainBit] Manager initialized successfully");
|
|
173
176
|
LabTools.Log($"[BrainBit] Config - AutoConnect: {_config.AutoConnectOnInit}, ScanTimeout: {_config.ScanTimeoutSeconds}s");
|
|
@@ -232,17 +235,59 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
232
235
|
}
|
|
233
236
|
#endregion
|
|
234
237
|
|
|
235
|
-
#region Public Methods
|
|
236
|
-
|
|
238
|
+
#region Public Methods
|
|
239
|
+
private bool EnsureInitialized(string callerName)
|
|
240
|
+
{
|
|
241
|
+
if (_isInitialized && _config != null)
|
|
242
|
+
{
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
try
|
|
247
|
+
{
|
|
248
|
+
_config ??= LabTools.GetConfig<BrainBitConfig>(true);
|
|
249
|
+
if (_config == null)
|
|
250
|
+
{
|
|
251
|
+
LabTools.LogError($"[BrainBit] {callerName} failed - BrainBitConfig is missing");
|
|
252
|
+
OnError?.Invoke("BrainBit config is missing");
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
_lastEEGData ??= new BrainBit_EEGData();
|
|
257
|
+
_lastImpedanceData ??= new BrainBit_ImpedanceData();
|
|
258
|
+
|
|
259
|
+
if (_connectionMonitorCoroutine == null)
|
|
260
|
+
{
|
|
261
|
+
_connectionMonitorCoroutine = StartCoroutine(MonitorConnection());
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
_isInitialized = true;
|
|
265
|
+
LabTools.Log($"[BrainBit] Initialization ready for {callerName}");
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
catch (Exception e)
|
|
269
|
+
{
|
|
270
|
+
LabTools.LogError($"[BrainBit] {callerName} failed during initialization: {e.Message}");
|
|
271
|
+
OnError?.Invoke($"Initialization failed: {e.Message}");
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/// <summary>
|
|
237
277
|
/// 開始掃描 BrainBit 設備
|
|
238
278
|
/// </summary>
|
|
239
|
-
public void StartScan()
|
|
240
|
-
{
|
|
241
|
-
if (
|
|
242
|
-
{
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
279
|
+
public void StartScan()
|
|
280
|
+
{
|
|
281
|
+
if (!EnsureInitialized(nameof(StartScan)))
|
|
282
|
+
{
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (IsScanning)
|
|
287
|
+
{
|
|
288
|
+
LabTools.LogError("[BrainBit] Already scanning for devices");
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
246
291
|
|
|
247
292
|
try
|
|
248
293
|
{
|
|
@@ -577,10 +622,18 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
577
622
|
}
|
|
578
623
|
}
|
|
579
624
|
|
|
580
|
-
private IEnumerator ScanTimeout()
|
|
581
|
-
{
|
|
582
|
-
|
|
583
|
-
|
|
625
|
+
private IEnumerator ScanTimeout()
|
|
626
|
+
{
|
|
627
|
+
if (!EnsureInitialized(nameof(ScanTimeout)))
|
|
628
|
+
{
|
|
629
|
+
IsScanning = false;
|
|
630
|
+
_scanTimeoutCoroutine = null;
|
|
631
|
+
yield break;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
float timeoutSeconds = _config.ScanTimeoutSeconds;
|
|
635
|
+
LabTools.Log($"[BrainBit] Scan timeout set for {timeoutSeconds} seconds");
|
|
636
|
+
yield return new WaitForSeconds(timeoutSeconds);
|
|
584
637
|
|
|
585
638
|
if (_scanTimeoutCoroutine != null && _currentSensor == null)
|
|
586
639
|
{
|
|
@@ -681,12 +734,18 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
681
734
|
/// 延遲連接設備
|
|
682
735
|
/// </summary>
|
|
683
736
|
/// <param name="sensorInfo">設備信息</param>
|
|
684
|
-
private IEnumerator DelayedConnect(SensorInfo sensorInfo)
|
|
685
|
-
{
|
|
686
|
-
|
|
737
|
+
private IEnumerator DelayedConnect(SensorInfo sensorInfo)
|
|
738
|
+
{
|
|
739
|
+
if (!EnsureInitialized(nameof(DelayedConnect)))
|
|
740
|
+
{
|
|
741
|
+
yield break;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
float connectDelaySeconds = _config.ConnectDelaySeconds;
|
|
745
|
+
LabTools.Log($"[BrainBit] Waiting {connectDelaySeconds}s before connecting...");
|
|
687
746
|
|
|
688
747
|
// 等待指定時間,確保掃描完全停止
|
|
689
|
-
yield return new WaitForSeconds(
|
|
748
|
+
yield return new WaitForSeconds(connectDelaySeconds);
|
|
690
749
|
|
|
691
750
|
// 建立連接
|
|
692
751
|
ConnectToDevice(sensorInfo);
|
|
@@ -777,12 +836,18 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
777
836
|
}
|
|
778
837
|
}
|
|
779
838
|
|
|
780
|
-
private void HandleDisconnection()
|
|
781
|
-
{
|
|
782
|
-
LabTools.LogError($"[BrainBit] Device disconnected: {ConnectedDeviceName}");
|
|
783
|
-
|
|
784
|
-
if (
|
|
785
|
-
{
|
|
839
|
+
private void HandleDisconnection()
|
|
840
|
+
{
|
|
841
|
+
LabTools.LogError($"[BrainBit] Device disconnected: {ConnectedDeviceName}");
|
|
842
|
+
|
|
843
|
+
if (!EnsureInitialized(nameof(HandleDisconnection)))
|
|
844
|
+
{
|
|
845
|
+
Disconnect();
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
if (_config.DisconnectNotification)
|
|
850
|
+
{
|
|
786
851
|
LabPromptBox.Show($"BrainBit 設備已斷線!\nDevice {ConnectedDeviceName} disconnected!");
|
|
787
852
|
}
|
|
788
853
|
|
|
@@ -795,12 +860,18 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
795
860
|
Disconnect();
|
|
796
861
|
}
|
|
797
862
|
|
|
798
|
-
private IEnumerator AttemptReconnect()
|
|
799
|
-
{
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
863
|
+
private IEnumerator AttemptReconnect()
|
|
864
|
+
{
|
|
865
|
+
if (!EnsureInitialized(nameof(AttemptReconnect)))
|
|
866
|
+
{
|
|
867
|
+
yield break;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
_reconnectAttempts++;
|
|
871
|
+
LabTools.Log($"[BrainBit] Attempting reconnection ({_reconnectAttempts}/{_config.AutoReconnectAttempts})...");
|
|
872
|
+
|
|
873
|
+
float reconnectIntervalSeconds = _config.ReconnectIntervalSeconds;
|
|
874
|
+
yield return new WaitForSeconds(reconnectIntervalSeconds);
|
|
804
875
|
|
|
805
876
|
if (!IsConnected)
|
|
806
877
|
{
|
|
@@ -908,12 +979,14 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
908
979
|
IsEmotionsCalibrated = false;
|
|
909
980
|
|
|
910
981
|
// 重置狀態
|
|
911
|
-
IsConnected = false;
|
|
912
|
-
IsStreamingEEG = false;
|
|
913
|
-
IsStreamingImpedance = false;
|
|
914
|
-
IsScanning = false;
|
|
915
|
-
|
|
916
|
-
|
|
982
|
+
IsConnected = false;
|
|
983
|
+
IsStreamingEEG = false;
|
|
984
|
+
IsStreamingImpedance = false;
|
|
985
|
+
IsScanning = false;
|
|
986
|
+
_isInitialized = false;
|
|
987
|
+
_autoConnectScheduled = false;
|
|
988
|
+
|
|
989
|
+
LabTools.Log("[BrainBit] Resources cleaned up");
|
|
917
990
|
}
|
|
918
991
|
catch (Exception e)
|
|
919
992
|
{
|
|
@@ -928,15 +1001,20 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
928
1001
|
/// 啟動情緒處理(MindData / SpectralData / 校正進度)。
|
|
929
1002
|
/// 若 EEG 串流未啟動,會自動啟動;呼叫 StopEmotionsProcessing 時會同步停止該 EEG 串流。
|
|
930
1003
|
/// </summary>
|
|
931
|
-
public void StartEmotionsProcessing(bool autoWriteToLabData = true,
|
|
932
|
-
string mindTag = "mind",
|
|
933
|
-
string spectralTag = "spectral")
|
|
934
|
-
{
|
|
935
|
-
if (!
|
|
936
|
-
{
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1004
|
+
public void StartEmotionsProcessing(bool autoWriteToLabData = true,
|
|
1005
|
+
string mindTag = "mind",
|
|
1006
|
+
string spectralTag = "spectral")
|
|
1007
|
+
{
|
|
1008
|
+
if (!EnsureInitialized(nameof(StartEmotionsProcessing)))
|
|
1009
|
+
{
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
if (!IsConnected)
|
|
1014
|
+
{
|
|
1015
|
+
LabTools.LogError("[BrainBit] Device not connected");
|
|
1016
|
+
OnError?.Invoke("Device not connected");
|
|
1017
|
+
return;
|
|
940
1018
|
}
|
|
941
1019
|
|
|
942
1020
|
if (IsProcessingEmotions)
|
|
@@ -948,8 +1026,8 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
948
1026
|
try
|
|
949
1027
|
{
|
|
950
1028
|
_autoWriteEmotionData = autoWriteToLabData;
|
|
951
|
-
_currentMindTag
|
|
952
|
-
_currentSpectralTag
|
|
1029
|
+
_currentMindTag = string.IsNullOrEmpty(mindTag) ? "mind" : mindTag;
|
|
1030
|
+
_currentSpectralTag = string.IsNullOrEmpty(spectralTag) ? "spectral" : spectralTag;
|
|
953
1031
|
|
|
954
1032
|
// 若 EEG 未啟動,自動啟動並記錄
|
|
955
1033
|
if (!IsStreamingEEG)
|
|
@@ -969,9 +1047,9 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
969
1047
|
|
|
970
1048
|
// 校正狀態重置
|
|
971
1049
|
IsEmotionsCalibrated = false;
|
|
972
|
-
CalibrationProgress
|
|
973
|
-
_lastMindData
|
|
974
|
-
_lastSpectralData
|
|
1050
|
+
CalibrationProgress = 0;
|
|
1051
|
+
_lastMindData = null;
|
|
1052
|
+
_lastSpectralData = null;
|
|
975
1053
|
|
|
976
1054
|
_emotionsController.StartCalibration();
|
|
977
1055
|
IsProcessingEmotions = true;
|
|
@@ -1010,8 +1088,8 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
1010
1088
|
}
|
|
1011
1089
|
|
|
1012
1090
|
_autoWriteEmotionData = false;
|
|
1013
|
-
IsEmotionsCalibrated
|
|
1014
|
-
CalibrationProgress
|
|
1091
|
+
IsEmotionsCalibrated = false;
|
|
1092
|
+
CalibrationProgress = 0;
|
|
1015
1093
|
|
|
1016
1094
|
if (_emotionsStartedEEG && IsStreamingEEG)
|
|
1017
1095
|
{
|
|
@@ -1039,7 +1117,7 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
1039
1117
|
}
|
|
1040
1118
|
|
|
1041
1119
|
IsEmotionsCalibrated = false;
|
|
1042
|
-
CalibrationProgress
|
|
1120
|
+
CalibrationProgress = 0;
|
|
1043
1121
|
_emotionsController.StartCalibration();
|
|
1044
1122
|
|
|
1045
1123
|
LabTools.Log("[BrainBit] Emotion calibration restarted");
|
|
@@ -1077,22 +1155,22 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
1077
1155
|
{
|
|
1078
1156
|
if (_emotionsController == null) return;
|
|
1079
1157
|
|
|
1080
|
-
_emotionsController.progressCalibrationCallback
|
|
1081
|
-
_emotionsController.lastMindDataCallback
|
|
1082
|
-
_emotionsController.lastSpectralDataCallback
|
|
1083
|
-
_emotionsController.isArtefactedSequenceCallback
|
|
1084
|
-
_emotionsController.isBothSidesArtifactedCallback
|
|
1158
|
+
_emotionsController.progressCalibrationCallback = OnEmotionsCalibrationProgress;
|
|
1159
|
+
_emotionsController.lastMindDataCallback = OnRawMindDataReceived;
|
|
1160
|
+
_emotionsController.lastSpectralDataCallback = OnRawSpectralDataReceived;
|
|
1161
|
+
_emotionsController.isArtefactedSequenceCallback = OnEmotionsArtifactDetected;
|
|
1162
|
+
_emotionsController.isBothSidesArtifactedCallback = OnEmotionsArtifactDetected;
|
|
1085
1163
|
}
|
|
1086
1164
|
|
|
1087
1165
|
private void UnwireEmotionsCallbacks()
|
|
1088
1166
|
{
|
|
1089
1167
|
if (_emotionsController == null) return;
|
|
1090
1168
|
|
|
1091
|
-
_emotionsController.progressCalibrationCallback
|
|
1092
|
-
_emotionsController.lastMindDataCallback
|
|
1093
|
-
_emotionsController.lastSpectralDataCallback
|
|
1094
|
-
_emotionsController.isArtefactedSequenceCallback
|
|
1095
|
-
_emotionsController.isBothSidesArtifactedCallback
|
|
1169
|
+
_emotionsController.progressCalibrationCallback = null;
|
|
1170
|
+
_emotionsController.lastMindDataCallback = null;
|
|
1171
|
+
_emotionsController.lastSpectralDataCallback = null;
|
|
1172
|
+
_emotionsController.isArtefactedSequenceCallback = null;
|
|
1173
|
+
_emotionsController.isBothSidesArtifactedCallback = null;
|
|
1096
1174
|
}
|
|
1097
1175
|
|
|
1098
1176
|
private void OnEmotionsCalibrationProgress(int progress)
|
|
@@ -1150,4 +1228,4 @@ public class BrainBitManager : LabSingleton<BrainBitManager>, IManager
|
|
|
1150
1228
|
}
|
|
1151
1229
|
|
|
1152
1230
|
#endregion
|
|
1153
|
-
}
|
|
1231
|
+
}
|
package/package.json
CHANGED