@@ -243,6 +243,10 @@ def __init__(self, parent=None, box_number=1, start_bonsai_ide=True):
243
243
# initialize thread lock
244
244
self .data_lock = threading .Lock ()
245
245
246
+ # intialize behavior baseline time flag
247
+ self .behavior_baseline_period = threading .Event ()
248
+ self .baseline_min_elapsed = 0
249
+
246
250
# create bias indicator
247
251
self .bias_n_size = 200
248
252
self .bias_indicator = BiasIndicator (
@@ -2761,6 +2765,7 @@ def keyPressEvent(self, event=None, allow_reset=False):
2761
2765
"laser_2_calibration_power" ,
2762
2766
"laser_1_calibration_voltage" ,
2763
2767
"laser_2_calibration_voltage" ,
2768
+ "hab_time_box"
2764
2769
}:
2765
2770
continue
2766
2771
if child .objectName () == "UncoupledReward" :
@@ -4006,11 +4011,11 @@ def _Save(self, ForceSave=0, SaveAs=0, SaveContinue=0, BackupSave=0):
4006
4011
):
4007
4012
self ._AddWaterLogResult (session )
4008
4013
elif self .BaseWeight .text () == "" or self .WeightAfter .text () == "" :
4009
- logging .error (f"Waterlog for mouse { self .behavior_session_model .subject } cannot be added to slims "
4010
- f" due do unrecorded weight information." )
4014
+ logging .warning (f"Waterlog for mouse { self .behavior_session_model .subject } cannot be added to slims"
4015
+ f" due do unrecorded weight information." )
4011
4016
elif session is None :
4012
- logging .error (f"Waterlog for mouse { self .behavior_session_model .subject } cannot be added to slims "
4013
- f" due do metadata generation failure." )
4017
+ logging .warning (f"Waterlog for mouse { self .behavior_session_model .subject } cannot be added to slims"
4018
+ f" due do metadata generation failure." )
4014
4019
except Exception as e :
4015
4020
logging .warning (
4016
4021
"Meta data is not saved!" ,
@@ -5456,6 +5461,7 @@ def _NewSession(self):
5456
5461
5457
5462
self .unsaved_data = False
5458
5463
self .ManualWaterVolume = [0 , 0 ]
5464
+ self .baseline_min_elapsed = 0 # variable to track baseline time elapsed before session for start/stop
5459
5465
5460
5466
# Clear Plots
5461
5467
if hasattr (self , "PlotM" ) and self .clear_figure_after_save :
@@ -5892,57 +5898,6 @@ def _Start(self):
5892
5898
elif self .behavior_session_model .allow_dirty_repo is None :
5893
5899
logging .error ("Could not check for untracked local changes" )
5894
5900
5895
- if self .PhotometryB .currentText () == "on" and (
5896
- not self .FIP_started
5897
- ):
5898
- reply = QMessageBox .critical (
5899
- self ,
5900
- "Box {}, Start" .format (self .box_letter ),
5901
- 'Photometry is set to "on", but the FIP workflow has not been started' ,
5902
- QMessageBox .Ok ,
5903
- )
5904
- self .Start .setChecked (False )
5905
- logging .info (
5906
- "Cannot start session without starting FIP workflow"
5907
- )
5908
- return
5909
-
5910
- # Check if photometry excitation is running or not
5911
- if self .PhotometryB .currentText () == "on" and (
5912
- not self .StartExcitation .isChecked ()
5913
- ):
5914
- logging .warning (
5915
- 'photometry is set to "on", but excitation is not running'
5916
- )
5917
-
5918
- reply = QMessageBox .question (
5919
- self ,
5920
- "Box {}, Start" .format (self .box_letter ),
5921
- 'Photometry is set to "on", but excitation is not running. Start excitation now?' ,
5922
- QMessageBox .Yes | QMessageBox .No ,
5923
- QMessageBox .Yes ,
5924
- )
5925
- if reply == QMessageBox .Yes :
5926
- self .StartExcitation .setChecked (True )
5927
- logging .info ("User selected to start excitation" )
5928
- started = self ._StartExcitation ()
5929
- if started == 0 :
5930
- reply = QMessageBox .critical (
5931
- self ,
5932
- "Box {}, Start" .format (self .box_letter ),
5933
- "Could not start excitation, therefore cannot start the session" ,
5934
- QMessageBox .Ok ,
5935
- )
5936
- logging .info (
5937
- "could not start session, due to failure to start excitation"
5938
- )
5939
- self .Start .setChecked (False )
5940
- return
5941
- else :
5942
- logging .info ("User selected not to start excitation" )
5943
- self .Start .setChecked (False )
5944
- return
5945
-
5946
5901
# disable sound button
5947
5902
self .sound_button .setEnabled (False )
5948
5903
@@ -5959,6 +5914,9 @@ def _Start(self):
5959
5914
self .session_run = True # session has been started
5960
5915
self .keyPressEvent (allow_reset = True )
5961
5916
5917
+ # set flag to perform habituation period
5918
+ self .behavior_baseline_period .set ()
5919
+
5962
5920
else :
5963
5921
# Prompt user to confirm stopping trials
5964
5922
reply = QMessageBox .question (
@@ -5990,6 +5948,7 @@ def _Start(self):
5990
5948
5991
5949
self .session_end_tasks ()
5992
5950
self .sound_button .setEnabled (True )
5951
+ self .behavior_baseline_period .clear () # set flag to break out of habituation period
5993
5952
5994
5953
if (self .StartANewSession == 1 ) and (self .ANewTrial == 0 ):
5995
5954
# If we are starting a new session, we should wait for the last trial to finish
@@ -6136,12 +6095,21 @@ def _Start(self):
6136
6095
workerStartTrialLoop1 = self .workerStartTrialLoop1
6137
6096
worker_save = self .worker_save
6138
6097
6098
+ # pause for specified habituation time
6099
+ if self .baseline_min_elapsed <= self .hab_time_box .value ():
6100
+ self .wait_for_baseline ()
6101
+
6139
6102
# collecting the base signal for photometry. Only run once
6140
6103
if (
6141
- self .Start .isChecked ()
6142
- and self .PhotometryB .currentText () == "on"
6143
- and self .PhotometryRun == 0
6104
+ self .Start .isChecked ()
6105
+ and self .PhotometryB .currentText () == "on"
6106
+ and self .PhotometryRun == 0
6144
6107
):
6108
+ # check if workflow is running and start photometry timer
6109
+ if not self .photometry_workflow_running ():
6110
+ self .Start .setChecked (False )
6111
+ return
6112
+
6145
6113
logging .info ("Starting photometry baseline timer" )
6146
6114
self .finish_Timer = 0
6147
6115
self .PhotometryRun = 1
@@ -6184,6 +6152,62 @@ def _Start(self):
6184
6152
except Exception :
6185
6153
logging .error (traceback .format_exc ())
6186
6154
6155
+ def photometry_workflow_running (self ) -> bool or None :
6156
+ """
6157
+ If fiber photometery is configured for session, check if work flow is running
6158
+
6159
+ :returns: boolean indicating if workflow is running or not. If None, fip is not configured
6160
+ """
6161
+
6162
+ if self .PhotometryB .currentText () == "on" and (
6163
+ not self .FIP_started
6164
+ ):
6165
+ reply = QMessageBox .critical (
6166
+ self ,
6167
+ "Box {}, Start" .format (self .box_letter ),
6168
+ 'Photometry is set to "on", but the FIP workflow has not been started' ,
6169
+ QMessageBox .Ok ,
6170
+ )
6171
+
6172
+ logging .info (
6173
+ "Cannot start session without starting FIP workflow"
6174
+ )
6175
+ return False
6176
+
6177
+ # Check if photometry excitation is running or not
6178
+ if self .PhotometryB .currentText () == "on" and not self .StartExcitation .isChecked ():
6179
+ logging .warning ('photometry is set to "on", but excitation is not running' )
6180
+
6181
+ reply = QMessageBox .question (
6182
+ self ,
6183
+ "Box {}, Start" .format (self .box_letter ),
6184
+ 'Photometry is set to "on", but excitation is not running. Start excitation now?' ,
6185
+ QMessageBox .Yes | QMessageBox .No ,
6186
+ QMessageBox .Yes ,
6187
+ )
6188
+ if reply == QMessageBox .Yes :
6189
+ self .StartExcitation .setChecked (True )
6190
+ logging .info ("User selected to start excitation" )
6191
+ started = self ._StartExcitation ()
6192
+ if started == 0 :
6193
+ reply = QMessageBox .critical (
6194
+ self ,
6195
+ "Box {}, Start" .format (self .box_letter ),
6196
+ "Could not start excitation, therefore cannot start the session" ,
6197
+ QMessageBox .Ok ,
6198
+ )
6199
+ logging .info (
6200
+ "could not start session, due to failure to start excitation"
6201
+ )
6202
+ self .Start .setChecked (False )
6203
+ return False
6204
+ else :
6205
+ logging .info ("User selected not to start excitation" )
6206
+ self .Start .setChecked (False )
6207
+ return False
6208
+
6209
+ return True
6210
+
6187
6211
def session_end_tasks (self ):
6188
6212
"""
6189
6213
Data cleanup and saving that needs to be done at end of session.
@@ -6265,17 +6289,53 @@ def end_session_log(self) -> None:
6265
6289
else :
6266
6290
logging .info ("No active session logger" )
6267
6291
6292
+ def wait_for_baseline (self ) -> None :
6293
+ """
6294
+ Function to wait for a baseline time before behavior
6295
+ """
6296
+
6297
+ # pause for specified habituation time
6298
+ start_time = time .time ()
6299
+
6300
+ # create habituation timer label and update every minute
6301
+ hab_lab = QLabel ()
6302
+ hab_lab .setStyleSheet (f"color: { self .default_warning_color } ;" )
6303
+ self .warning_widget .layout ().insertWidget (0 , hab_lab )
6304
+ update_hab_timer = QtCore .QTimer (
6305
+ timeout = lambda : hab_lab .setText (f"Time elapsed: "
6306
+ f"{ round ((self .baseline_min_elapsed * 60 ) // 60 )} minutes"
6307
+ f" { round ((self .baseline_min_elapsed * 60 ) % 60 )} seconds" ),
6308
+ interval = 1000 )
6309
+ update_hab_timer .start ()
6310
+
6311
+ logging .info (f"Waiting { round (self .hab_time_box .value () - self .baseline_min_elapsed )} min before starting "
6312
+ f"session." )
6313
+
6314
+ elapsed = self .baseline_min_elapsed
6315
+ while self .baseline_min_elapsed < self .hab_time_box .value () and self .behavior_baseline_period .is_set ():
6316
+ QApplication .processEvents ()
6317
+ # update baseline time elapsed before session for start/stop logic
6318
+ self .baseline_min_elapsed = ((time .time () - start_time ) / 60 ) + elapsed
6319
+
6320
+ update_hab_timer .stop ()
6321
+ self .behavior_baseline_period .clear ()
6322
+
6323
+
6268
6324
def _StartTrialLoop (self , GeneratedTrials , worker1 , worker_save ):
6269
- if self .Start .isChecked ():
6270
- logging .info ("starting trial loop" )
6271
- else :
6325
+
6326
+ if not self .Start .isChecked ():
6272
6327
logging .info ("ending trial loop" )
6328
+ return
6329
+
6330
+ logging .info ("starting trial loop" )
6273
6331
6274
6332
# Track elapsed time in case Bonsai Stalls
6275
6333
last_trial_start = time .time ()
6276
6334
stall_iteration = 1
6277
6335
stall_duration = 5 * 60
6278
6336
6337
+ logging .info (f"Starting session." )
6338
+
6279
6339
while self .Start .isChecked ():
6280
6340
QApplication .processEvents ()
6281
6341
if (
0 commit comments