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.
@@ -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 _autoWriteEmotionData = false;
143
- private string _currentMindTag = "mind";
144
- private string _currentSpectralTag = "spectral";
145
- private BrainBit_MindData _lastMindData;
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
- _config = LabTools.GetConfig<BrainBitConfig>(true);
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
- StartCoroutine(DelayedAutoConnect());
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
- /// <summary>
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 (IsScanning)
242
- {
243
- LabTools.LogError("[BrainBit] Already scanning for devices");
244
- return;
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
- LabTools.Log($"[BrainBit] Scan timeout set for {_config.ScanTimeoutSeconds} seconds");
583
- yield return new WaitForSeconds(_config.ScanTimeoutSeconds);
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
- LabTools.Log($"[BrainBit] Waiting {_config.ConnectDelaySeconds}s before connecting...");
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(_config.ConnectDelaySeconds);
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 (_config.DisconnectNotification)
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
- _reconnectAttempts++;
801
- LabTools.Log($"[BrainBit] Attempting reconnection ({_reconnectAttempts}/{_config.AutoReconnectAttempts})...");
802
-
803
- yield return new WaitForSeconds(_config.ReconnectIntervalSeconds);
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
- LabTools.Log("[BrainBit] Resources cleaned up");
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 (!IsConnected)
936
- {
937
- LabTools.LogError("[BrainBit] Device not connected");
938
- OnError?.Invoke("Device not connected");
939
- return;
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 = string.IsNullOrEmpty(mindTag) ? "mind" : mindTag;
952
- _currentSpectralTag = string.IsNullOrEmpty(spectralTag) ? "spectral" : spectralTag;
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 = 0;
973
- _lastMindData = null;
974
- _lastSpectralData = null;
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 = false;
1014
- CalibrationProgress = 0;
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 = 0;
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 = OnEmotionsCalibrationProgress;
1081
- _emotionsController.lastMindDataCallback = OnRawMindDataReceived;
1082
- _emotionsController.lastSpectralDataCallback = OnRawSpectralDataReceived;
1083
- _emotionsController.isArtefactedSequenceCallback = OnEmotionsArtifactDetected;
1084
- _emotionsController.isBothSidesArtifactedCallback = OnEmotionsArtifactDetected;
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 = null;
1092
- _emotionsController.lastMindDataCallback = null;
1093
- _emotionsController.lastSpectralDataCallback = null;
1094
- _emotionsController.isArtefactedSequenceCallback = null;
1095
- _emotionsController.isBothSidesArtifactedCallback = null;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.xrlab.labframe_brainbit",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "displayName": "Lab Frame 2023 - BrainBit Plugin",
5
5
  "description": "BrainBit Support for LabFrame2023.\nNote: Currently only supports BrainBit in lab!!",
6
6
  "unity": "2022.3",