np-workflows 1.6.87__py3-none-any.whl → 1.6.91__py3-none-any.whl
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.
- np_workflows/__init__.py +3 -5
- np_workflows/experiments/dynamic_routing/main.py +20 -41
- np_workflows/experiments/dynamic_routing/widgets.py +29 -24
- np_workflows/experiments/openscope_P3/P3_workflow_widget.py +75 -0
- np_workflows/experiments/openscope_P3/__init__.py +2 -0
- np_workflows/experiments/openscope_P3/main_P3_pilot.py +215 -0
- np_workflows/experiments/openscope_barcode/__init__.py +1 -1
- np_workflows/experiments/openscope_barcode/barcode_workflow_widget.py +14 -20
- np_workflows/experiments/openscope_barcode/camstim_scripts/barcode_mapping_script.py +8 -14
- np_workflows/experiments/openscope_barcode/camstim_scripts/barcode_opto_script.py +121 -68
- np_workflows/experiments/openscope_barcode/main_barcode_pilot.py +69 -69
- np_workflows/experiments/openscope_loop/__init__.py +1 -1
- np_workflows/experiments/openscope_loop/camstim_scripts/barcode_mapping_script.py +8 -14
- np_workflows/experiments/openscope_loop/camstim_scripts/barcode_opto_script.py +121 -68
- np_workflows/experiments/openscope_loop/loop_workflow_widget.py +11 -19
- np_workflows/experiments/openscope_loop/main_loop_pilot.py +66 -68
- np_workflows/experiments/openscope_psycode/__init__.py +1 -1
- np_workflows/experiments/openscope_psycode/main_psycode_pilot.py +69 -69
- np_workflows/experiments/openscope_psycode/psycode_workflow_widget.py +14 -20
- np_workflows/experiments/openscope_v2/__init__.py +2 -0
- np_workflows/experiments/openscope_v2/main_v2_pilot.py +215 -0
- np_workflows/experiments/openscope_v2/v2_workflow_widget.py +75 -0
- np_workflows/experiments/openscope_vippo/__init__.py +1 -1
- np_workflows/experiments/openscope_vippo/main_vippo_pilot.py +66 -68
- np_workflows/experiments/openscope_vippo/vippo_workflow_widget.py +14 -20
- np_workflows/experiments/task_trained_network/__init__.py +1 -1
- np_workflows/experiments/task_trained_network/camstim_scripts/make_tt_stims.py +24 -14
- np_workflows/experiments/task_trained_network/camstim_scripts/oct22_tt_stim_script.py +54 -41
- np_workflows/experiments/task_trained_network/camstim_scripts/ttn_main_script.py +19 -22
- np_workflows/experiments/task_trained_network/camstim_scripts/ttn_mapping_script.py +8 -14
- np_workflows/experiments/task_trained_network/camstim_scripts/ttn_opto_script.py +121 -68
- np_workflows/experiments/task_trained_network/main_ttn_pilot.py +73 -68
- np_workflows/experiments/task_trained_network/ttn_session_widget.py +11 -19
- np_workflows/experiments/task_trained_network/ttn_stim_config.py +23 -19
- np_workflows/experiments/templeton/main.py +18 -41
- np_workflows/experiments/templeton/widgets.py +26 -23
- np_workflows/shared/__init__.py +1 -1
- np_workflows/shared/base_experiments.py +430 -308
- np_workflows/shared/npxc.py +85 -53
- np_workflows/shared/widgets.py +374 -224
- {np_workflows-1.6.87.dist-info → np_workflows-1.6.91.dist-info}/METADATA +7 -21
- np_workflows-1.6.91.dist-info/RECORD +48 -0
- {np_workflows-1.6.87.dist-info → np_workflows-1.6.91.dist-info}/WHEEL +2 -1
- np_workflows-1.6.91.dist-info/entry_points.txt +2 -0
- np_workflows-1.6.91.dist-info/top_level.txt +1 -0
- np_workflows/assets/images/logo_np_hab.png +0 -0
- np_workflows/assets/images/logo_np_vis.png +0 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_00.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_01.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_02.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_03.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_04.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_05.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_06.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_07.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_08.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_09.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_10.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_11.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_12.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_13.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_14.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_15.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_16.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_17.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_18.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/flash_250ms.stim +0 -20
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/gabor_20_deg_250ms.stim +0 -30
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/old_stim.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/shuffle_reversed.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/shuffle_reversed_1st.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/shuffle_reversed_2nd.stim +0 -5
- np_workflows/shared/camstim_scripts/flash_250ms.stim +0 -20
- np_workflows/shared/camstim_scripts/gabor_20_deg_250ms.stim +0 -30
- np_workflows-1.6.87.dist-info/RECORD +0 -70
- np_workflows-1.6.87.dist-info/entry_points.txt +0 -4
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
"""
|
|
3
2
|
optotagging.py
|
|
4
3
|
|
|
@@ -9,37 +8,30 @@ by joshs@alleninstitute.org
|
|
|
9
8
|
(c) 2018 Allen Institute for Brain Science
|
|
10
9
|
|
|
11
10
|
"""
|
|
12
|
-
import camstim # ensures "magic" gets setup properly by importing first
|
|
13
|
-
import logging # must occur after camstim import for "magic"
|
|
14
|
-
from camstim.zro import agent
|
|
15
|
-
|
|
16
|
-
import numpy as np
|
|
17
|
-
from toolbox.IO.nidaq import AnalogOutput
|
|
18
|
-
from toolbox.IO.nidaq import DigitalOutput
|
|
19
11
|
|
|
20
12
|
import datetime
|
|
21
|
-
import
|
|
22
|
-
import time
|
|
13
|
+
import logging # must occur after camstim import for "magic"
|
|
23
14
|
import pickle as pkl
|
|
15
|
+
import time
|
|
24
16
|
|
|
17
|
+
import numpy as np
|
|
18
|
+
from camstim.zro import agent
|
|
19
|
+
from toolbox.IO.nidaq import AnalogOutput, DigitalOutput
|
|
25
20
|
|
|
26
21
|
# %%
|
|
27
22
|
|
|
28
23
|
|
|
29
|
-
def run_optotagging(levels, conditions, waveforms, isis, sampleRate=10000.):
|
|
30
|
-
|
|
31
|
-
from toolbox.IO.nidaq import AnalogOutput
|
|
32
|
-
from toolbox.IO.nidaq import DigitalOutput
|
|
24
|
+
def run_optotagging(levels, conditions, waveforms, isis, sampleRate=10000.0):
|
|
33
25
|
|
|
34
26
|
sweep_on = np.array([0, 0, 1, 0, 0, 0, 0, 0], dtype=np.uint8)
|
|
35
27
|
stim_on = np.array([0, 0, 1, 1, 0, 0, 0, 0], dtype=np.uint8)
|
|
36
28
|
stim_off = np.array([0, 0, 1, 0, 0, 0, 0, 0], dtype=np.uint8)
|
|
37
29
|
sweep_off = np.array([0, 0, 0, 0, 0, 0, 0, 0], dtype=np.uint8)
|
|
38
30
|
|
|
39
|
-
ao = AnalogOutput(
|
|
31
|
+
ao = AnalogOutput("Dev1", channels=[1])
|
|
40
32
|
ao.cfg_sample_clock(sampleRate)
|
|
41
33
|
|
|
42
|
-
do = DigitalOutput(
|
|
34
|
+
do = DigitalOutput("Dev1", 2)
|
|
43
35
|
|
|
44
36
|
do.start()
|
|
45
37
|
ao.start()
|
|
@@ -62,71 +54,123 @@ def run_optotagging(levels, conditions, waveforms, isis, sampleRate=10000.):
|
|
|
62
54
|
do.clear()
|
|
63
55
|
ao.clear()
|
|
64
56
|
|
|
57
|
+
|
|
65
58
|
# %%
|
|
66
59
|
|
|
67
60
|
|
|
68
|
-
def generatePulseTrain(
|
|
61
|
+
def generatePulseTrain(
|
|
62
|
+
pulseWidth, pulseInterval, numRepeats, riseTime, sampleRate=10000.0
|
|
63
|
+
):
|
|
69
64
|
|
|
70
65
|
data = np.zeros((int(sampleRate),), dtype=np.float64)
|
|
71
|
-
|
|
66
|
+
# rise_samples =
|
|
72
67
|
|
|
73
68
|
rise_and_fall = (
|
|
74
|
-
(
|
|
69
|
+
(
|
|
70
|
+
(
|
|
71
|
+
1
|
|
72
|
+
- np.cos(
|
|
73
|
+
np.arange(sampleRate * riseTime / 1000.0, dtype=np.float64)
|
|
74
|
+
* 2
|
|
75
|
+
* np.pi
|
|
76
|
+
/ 10
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
+ 1
|
|
80
|
+
)
|
|
81
|
+
- 1
|
|
82
|
+
) / 2
|
|
75
83
|
half_length = rise_and_fall.size / 2
|
|
76
84
|
rise = rise_and_fall[:half_length]
|
|
77
85
|
fall = rise_and_fall[half_length:]
|
|
78
86
|
|
|
79
|
-
peak_samples = int(sampleRate*(pulseWidth-riseTime*2)/1000)
|
|
87
|
+
peak_samples = int(sampleRate * (pulseWidth - riseTime * 2) / 1000)
|
|
80
88
|
peak = np.ones((peak_samples,))
|
|
81
89
|
|
|
82
|
-
pulse = np.concatenate((rise,
|
|
83
|
-
peak,
|
|
84
|
-
fall))
|
|
90
|
+
pulse = np.concatenate((rise, peak, fall))
|
|
85
91
|
|
|
86
|
-
interval = int(pulseInterval*sampleRate/1000.)
|
|
92
|
+
interval = int(pulseInterval * sampleRate / 1000.0)
|
|
87
93
|
|
|
88
94
|
for i in range(0, numRepeats):
|
|
89
|
-
data[i*interval:i*interval+pulse.size] = pulse
|
|
95
|
+
data[i * interval : i * interval + pulse.size] = pulse
|
|
90
96
|
|
|
91
97
|
return data
|
|
92
98
|
|
|
93
99
|
|
|
94
100
|
# %% create waveforms
|
|
95
101
|
|
|
96
|
-
|
|
102
|
+
|
|
103
|
+
def optotagging(
|
|
104
|
+
mouseID, operation_mode="experiment", level_list=[1.15, 1.28, 1.345], genotype=None
|
|
105
|
+
):
|
|
97
106
|
|
|
98
107
|
sampleRate = 10000
|
|
99
108
|
|
|
100
109
|
# 1 s cosine ramp:
|
|
101
|
-
data_cosine = (
|
|
102
|
-
|
|
110
|
+
data_cosine = (
|
|
111
|
+
(
|
|
112
|
+
(
|
|
113
|
+
1
|
|
114
|
+
- np.cos(
|
|
115
|
+
np.arange(sampleRate, dtype=np.float64) * 2 * np.pi / sampleRate
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
+ 1
|
|
119
|
+
)
|
|
120
|
+
- 1
|
|
121
|
+
) / 2 # create raised cosine waveform
|
|
103
122
|
|
|
104
123
|
# 1 ms cosine ramp:
|
|
105
124
|
rise_and_fall = (
|
|
106
|
-
(
|
|
125
|
+
(
|
|
126
|
+
(
|
|
127
|
+
1
|
|
128
|
+
- np.cos(
|
|
129
|
+
np.arange(sampleRate * 0.001, dtype=np.float64) * 2 * np.pi / 10
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
+ 1
|
|
133
|
+
)
|
|
134
|
+
- 1
|
|
135
|
+
) / 2
|
|
107
136
|
half_length = rise_and_fall.size / 2
|
|
108
137
|
|
|
109
138
|
# pulses with cosine ramp:
|
|
110
|
-
pulse_2ms = np.concatenate(
|
|
111
|
-
(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
139
|
+
pulse_2ms = np.concatenate(
|
|
140
|
+
(
|
|
141
|
+
rise_and_fall[:half_length],
|
|
142
|
+
np.ones((int(sampleRate * 0.001),)),
|
|
143
|
+
rise_and_fall[half_length:],
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
pulse_5ms = np.concatenate(
|
|
147
|
+
(
|
|
148
|
+
rise_and_fall[:half_length],
|
|
149
|
+
np.ones((int(sampleRate * 0.004),)),
|
|
150
|
+
rise_and_fall[half_length:],
|
|
151
|
+
)
|
|
152
|
+
)
|
|
153
|
+
pulse_10ms = np.concatenate(
|
|
154
|
+
(
|
|
155
|
+
rise_and_fall[:half_length],
|
|
156
|
+
np.ones((int(sampleRate * 0.009),)),
|
|
157
|
+
rise_and_fall[half_length:],
|
|
158
|
+
)
|
|
159
|
+
)
|
|
116
160
|
|
|
117
161
|
data_2ms_10Hz = np.zeros((sampleRate,), dtype=np.float64)
|
|
118
162
|
|
|
119
163
|
for i in range(0, 10):
|
|
120
164
|
interval = sampleRate / 10
|
|
121
|
-
data_2ms_10Hz[i*interval:i*interval+pulse_2ms.size] = pulse_2ms
|
|
165
|
+
data_2ms_10Hz[i * interval : i * interval + pulse_2ms.size] = pulse_2ms
|
|
122
166
|
|
|
123
167
|
data_5ms = np.zeros((sampleRate,), dtype=np.float64)
|
|
124
|
-
data_5ms[:pulse_5ms.size] = pulse_5ms
|
|
168
|
+
data_5ms[: pulse_5ms.size] = pulse_5ms
|
|
125
169
|
|
|
126
170
|
data_10ms = np.zeros((sampleRate,), dtype=np.float64)
|
|
127
|
-
data_10ms[:pulse_10ms.size] = pulse_10ms
|
|
171
|
+
data_10ms[: pulse_10ms.size] = pulse_10ms
|
|
128
172
|
|
|
129
|
-
data_10s = np.zeros((sampleRate*10,), dtype=np.float64)
|
|
173
|
+
data_10s = np.zeros((sampleRate * 10,), dtype=np.float64)
|
|
130
174
|
data_10s[:-2] = 1
|
|
131
175
|
|
|
132
176
|
# %% for experiment
|
|
@@ -138,8 +182,8 @@ def optotagging(mouseID, operation_mode='experiment', level_list=[1.15, 1.28, 1.
|
|
|
138
182
|
condition_list = [2, 3]
|
|
139
183
|
waveforms = [data_2ms_10Hz, data_5ms, data_10ms, data_cosine]
|
|
140
184
|
|
|
141
|
-
opto_levels = np.array(level_list*numRepeats*len(condition_list)) # BLUE
|
|
142
|
-
opto_conditions = condition_list*numRepeats*len(level_list)
|
|
185
|
+
opto_levels = np.array(level_list * numRepeats * len(condition_list)) # BLUE
|
|
186
|
+
opto_conditions = condition_list * numRepeats * len(level_list)
|
|
143
187
|
opto_conditions = np.sort(opto_conditions)
|
|
144
188
|
opto_isis = np.random.random(opto_levels.shape) * isi_rand + isi
|
|
145
189
|
|
|
@@ -151,7 +195,7 @@ def optotagging(mouseID, operation_mode='experiment', level_list=[1.15, 1.28, 1.
|
|
|
151
195
|
|
|
152
196
|
# %% for testing
|
|
153
197
|
|
|
154
|
-
if operation_mode ==
|
|
198
|
+
if operation_mode == "test_levels":
|
|
155
199
|
isi = 2.0
|
|
156
200
|
isi_rand = 0.0
|
|
157
201
|
|
|
@@ -160,60 +204,69 @@ def optotagging(mouseID, operation_mode='experiment', level_list=[1.15, 1.28, 1.
|
|
|
160
204
|
condition_list = [0]
|
|
161
205
|
waveforms = [data_10s, data_10s]
|
|
162
206
|
|
|
163
|
-
opto_levels = np.array(level_list*numRepeats *
|
|
164
|
-
|
|
165
|
-
opto_conditions = condition_list*numRepeats*len(level_list)
|
|
207
|
+
opto_levels = np.array(level_list * numRepeats * len(condition_list)) # BLUE
|
|
208
|
+
opto_conditions = condition_list * numRepeats * len(level_list)
|
|
166
209
|
opto_conditions = np.sort(opto_conditions)
|
|
167
210
|
opto_isis = np.random.random(opto_levels.shape) * isi_rand + isi
|
|
168
211
|
|
|
169
|
-
elif operation_mode ==
|
|
212
|
+
elif operation_mode == "pretest":
|
|
170
213
|
numRepeats = 1
|
|
171
214
|
|
|
172
215
|
condition_list = [0]
|
|
173
|
-
data_2s = data_10s[-sampleRate*2:]
|
|
216
|
+
data_2s = data_10s[-sampleRate * 2 :]
|
|
174
217
|
waveforms = [data_2s]
|
|
175
218
|
|
|
176
|
-
opto_levels = np.array(level_list*numRepeats *
|
|
177
|
-
|
|
178
|
-
opto_conditions = condition_list*numRepeats*len(level_list)
|
|
219
|
+
opto_levels = np.array(level_list * numRepeats * len(condition_list)) # BLUE
|
|
220
|
+
opto_conditions = condition_list * numRepeats * len(level_list)
|
|
179
221
|
opto_conditions = np.sort(opto_conditions)
|
|
180
|
-
opto_isis = [1]*len(opto_conditions)
|
|
222
|
+
opto_isis = [1] * len(opto_conditions)
|
|
181
223
|
# %%
|
|
182
224
|
|
|
183
225
|
outputDirectory = agent.OUTPUT_DIR
|
|
184
|
-
fileDate =
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
226
|
+
fileDate = (
|
|
227
|
+
str(datetime.datetime.now())
|
|
228
|
+
.replace(":", "")
|
|
229
|
+
.replace(".", "")
|
|
230
|
+
.replace("-", "")
|
|
231
|
+
.replace(" ", "")[2:14]
|
|
232
|
+
)
|
|
233
|
+
fileName = outputDirectory + "/" + fileDate + "_" + mouseID + ".opto.pkl"
|
|
234
|
+
|
|
235
|
+
print("saving info to: " + fileName)
|
|
236
|
+
fl = open(fileName, "wb")
|
|
190
237
|
output = {}
|
|
191
238
|
|
|
192
|
-
output[
|
|
193
|
-
output[
|
|
194
|
-
output[
|
|
195
|
-
output[
|
|
239
|
+
output["opto_levels"] = opto_levels
|
|
240
|
+
output["opto_conditions"] = opto_conditions
|
|
241
|
+
output["opto_ISIs"] = opto_isis
|
|
242
|
+
output["opto_waveforms"] = waveforms
|
|
196
243
|
|
|
197
244
|
pkl.dump(output, fl)
|
|
198
245
|
fl.close()
|
|
199
|
-
print(
|
|
246
|
+
print("saved.")
|
|
200
247
|
|
|
201
248
|
# %%
|
|
202
|
-
run_optotagging(
|
|
203
|
-
|
|
249
|
+
run_optotagging(
|
|
250
|
+
opto_levels, opto_conditions, waveforms, opto_isis, float(sampleRate)
|
|
251
|
+
)
|
|
204
252
|
|
|
205
253
|
|
|
206
254
|
# %%
|
|
207
255
|
if __name__ == "__main__":
|
|
208
|
-
import json
|
|
209
256
|
import argparse
|
|
257
|
+
import json
|
|
210
258
|
|
|
211
259
|
parser = argparse.ArgumentParser()
|
|
212
|
-
parser.add_argument(
|
|
260
|
+
parser.add_argument(
|
|
261
|
+
"json_params",
|
|
262
|
+
type=str,
|
|
263
|
+
)
|
|
213
264
|
args, _ = parser.parse_known_args()
|
|
214
265
|
|
|
215
|
-
with open(
|
|
266
|
+
with open(
|
|
267
|
+
args.json_params,
|
|
268
|
+
) as f:
|
|
216
269
|
json_params = json.load(f)
|
|
217
270
|
|
|
218
|
-
logging.info(
|
|
271
|
+
logging.info("Optotagging with params: %s" % json_params)
|
|
219
272
|
optotagging(**json_params)
|
|
@@ -1,39 +1,28 @@
|
|
|
1
|
-
import configparser
|
|
2
1
|
import contextlib
|
|
3
2
|
import copy
|
|
4
|
-
import dataclasses
|
|
5
|
-
import datetime
|
|
6
|
-
import enum
|
|
7
3
|
import functools
|
|
8
4
|
import pathlib
|
|
9
|
-
import platform
|
|
10
5
|
import shutil
|
|
11
|
-
import threading
|
|
12
6
|
import time
|
|
13
7
|
import zlib
|
|
14
|
-
from typing import
|
|
8
|
+
from typing import Literal
|
|
15
9
|
|
|
16
|
-
import IPython
|
|
17
|
-
import IPython.display
|
|
18
|
-
import ipywidgets as ipw
|
|
19
10
|
import np_config
|
|
20
11
|
import np_logging
|
|
21
|
-
import np_services
|
|
22
12
|
import np_session
|
|
23
|
-
import np_workflows
|
|
24
|
-
import PIL.Image
|
|
25
|
-
import pydantic
|
|
26
|
-
from pyparsing import Any
|
|
27
13
|
from np_services import (
|
|
28
|
-
Service,
|
|
29
14
|
Finalizable,
|
|
30
|
-
|
|
15
|
+
MouseDirector,
|
|
16
|
+
NewScaleCoordinateRecorder,
|
|
31
17
|
OpenEphys,
|
|
18
|
+
ScriptCamstim,
|
|
19
|
+
Service,
|
|
32
20
|
Sync,
|
|
33
21
|
VideoMVR,
|
|
34
|
-
NewScaleCoordinateRecorder,
|
|
35
|
-
MouseDirector,
|
|
36
22
|
)
|
|
23
|
+
from pyparsing import Any
|
|
24
|
+
|
|
25
|
+
import np_workflows
|
|
37
26
|
|
|
38
27
|
from .ttn_stim_config import (
|
|
39
28
|
TTNSession,
|
|
@@ -41,6 +30,8 @@ from .ttn_stim_config import (
|
|
|
41
30
|
per_session_main_stim_params,
|
|
42
31
|
per_session_mapping_params,
|
|
43
32
|
per_session_opto_params,
|
|
33
|
+
)
|
|
34
|
+
from .ttn_stim_config import (
|
|
44
35
|
default_ttn_params as DEFAULT_STIM_PARAMS,
|
|
45
36
|
)
|
|
46
37
|
|
|
@@ -49,36 +40,36 @@ logger = np_logging.getLogger(__name__)
|
|
|
49
40
|
|
|
50
41
|
class TTNMixin:
|
|
51
42
|
"""Provides TTN-specific methods and attributes, mainly related to camstim scripts."""
|
|
52
|
-
|
|
43
|
+
|
|
53
44
|
ttn_session: TTNSession
|
|
54
45
|
"""Enum for session type, e.g. PRETEST, HAB_60, HAB_90, EPHYS."""
|
|
55
46
|
|
|
56
47
|
@property
|
|
57
48
|
def script_root_on_stim(self) -> pathlib.Path:
|
|
58
49
|
"Path to local copy on Stim, from Stim."
|
|
59
|
-
return pathlib.Path(
|
|
60
|
-
|
|
50
|
+
return pathlib.Path("C:/ProgramData/StimulusFiles/dev")
|
|
51
|
+
|
|
61
52
|
@property
|
|
62
53
|
def script_root_on_local(self) -> pathlib.Path:
|
|
63
54
|
"Path to version controlled scripts on local machine."
|
|
64
|
-
return
|
|
65
|
-
|
|
55
|
+
return pathlib.Path(__file__).parent / "camstim_scripts"
|
|
56
|
+
|
|
66
57
|
@property
|
|
67
58
|
def script_names(self) -> dict[Literal["main", "mapping", "opto"], str]:
|
|
68
59
|
return {
|
|
69
60
|
label: f"ttn_{label}_script.py" for label in ("main", "mapping", "opto")
|
|
70
61
|
}
|
|
71
|
-
|
|
62
|
+
|
|
72
63
|
@property
|
|
73
64
|
def stim_root_on_stim(self) -> pathlib.Path:
|
|
74
65
|
"Path to dev folder on Stim computer, as seen from local machine."
|
|
75
66
|
return np_config.local_to_unc(self.rig.stim, self.script_root_on_stim)
|
|
76
|
-
|
|
67
|
+
|
|
77
68
|
@property
|
|
78
69
|
def stim_root_on_local(self) -> pathlib.Path:
|
|
79
70
|
"Path to version controlled stim files on local machine."
|
|
80
|
-
return self.script_root_on_local /
|
|
81
|
-
|
|
71
|
+
return self.script_root_on_local / "stims"
|
|
72
|
+
|
|
82
73
|
@property
|
|
83
74
|
def recorders(self) -> tuple[Service, ...]:
|
|
84
75
|
"""Services to be started before stimuli run, and stopped after. Session-dependent."""
|
|
@@ -91,10 +82,10 @@ class TTNMixin:
|
|
|
91
82
|
@property
|
|
92
83
|
def stims(self) -> tuple[Service, ...]:
|
|
93
84
|
return (ScriptCamstim,)
|
|
94
|
-
|
|
85
|
+
|
|
95
86
|
def initialize_and_test_services(self) -> None:
|
|
96
87
|
"""Configure, initialize (ie. reset), then test all services."""
|
|
97
|
-
|
|
88
|
+
|
|
98
89
|
MouseDirector.user = self.user.id
|
|
99
90
|
MouseDirector.mouse = self.mouse.id
|
|
100
91
|
|
|
@@ -107,59 +98,68 @@ class TTNMixin:
|
|
|
107
98
|
|
|
108
99
|
super().initialize_and_test_services()
|
|
109
100
|
|
|
110
|
-
|
|
111
101
|
def update_state(self) -> None:
|
|
112
102
|
"Store useful but non-essential info."
|
|
113
|
-
self.mouse.state[
|
|
114
|
-
self.mouse.state[
|
|
103
|
+
self.mouse.state["last_session"] = self.session.id
|
|
104
|
+
self.mouse.state["last_ttn_session"] = str(self.ttn_session)
|
|
115
105
|
if self.mouse == 366122:
|
|
116
106
|
return
|
|
117
107
|
match self.ttn_session:
|
|
118
108
|
case TTNSession.PRETEST:
|
|
119
109
|
return
|
|
120
110
|
case TTNSession.HAB_60 | TTNSession.HAB_90 | TTNSession.HAB_120:
|
|
121
|
-
self.session.project.state[
|
|
111
|
+
self.session.project.state["latest_hab"] = self.session.id
|
|
122
112
|
case TTNSession.EPHYS:
|
|
123
|
-
self.session.project.state[
|
|
124
|
-
self.session.project.state[
|
|
125
|
-
|
|
113
|
+
self.session.project.state["latest_ephys"] = self.session.id
|
|
114
|
+
self.session.project.state["sessions"] = self.session.project.state.get(
|
|
115
|
+
"sessions", []
|
|
116
|
+
) + [self.session.id]
|
|
117
|
+
|
|
126
118
|
def run_stim_scripts(self) -> None:
|
|
127
119
|
self.validate_or_copy_stim_files()
|
|
128
120
|
self.update_state()
|
|
129
|
-
|
|
130
|
-
for stim in (
|
|
131
|
-
|
|
121
|
+
|
|
122
|
+
for stim in ("mapping", "main", "opto"):
|
|
123
|
+
|
|
132
124
|
if not (params := self.params[stim]):
|
|
133
|
-
logger.info(
|
|
125
|
+
logger.info(
|
|
126
|
+
"%s script skipped this session: %r", stim, self.ttn_session
|
|
127
|
+
)
|
|
134
128
|
continue
|
|
135
|
-
|
|
129
|
+
|
|
136
130
|
ScriptCamstim.params = params
|
|
137
131
|
ScriptCamstim.script = self.scripts[stim]
|
|
138
|
-
|
|
132
|
+
|
|
139
133
|
logger.debug("Starting %s script", stim)
|
|
140
|
-
|
|
134
|
+
|
|
141
135
|
ScriptCamstim.start()
|
|
142
|
-
|
|
136
|
+
|
|
143
137
|
with contextlib.suppress(Exception):
|
|
144
|
-
np_logging.web(f
|
|
145
|
-
|
|
138
|
+
np_logging.web(f"ttn_{self.ttn_session.name.lower()}").info(
|
|
139
|
+
f"{stim.capitalize()} stim started"
|
|
140
|
+
)
|
|
141
|
+
|
|
146
142
|
with contextlib.suppress(Exception):
|
|
147
143
|
while not ScriptCamstim.is_ready_to_start():
|
|
148
144
|
time.sleep(2.5)
|
|
149
|
-
|
|
145
|
+
|
|
150
146
|
if isinstance(ScriptCamstim, Finalizable):
|
|
151
147
|
ScriptCamstim.finalize()
|
|
152
148
|
|
|
153
149
|
with contextlib.suppress(Exception):
|
|
154
|
-
np_logging.web(f
|
|
155
|
-
|
|
150
|
+
np_logging.web(f"ttn_{self.ttn_session.name.lower()}").info(
|
|
151
|
+
f"{stim.capitalize()} stim finished"
|
|
152
|
+
)
|
|
153
|
+
|
|
156
154
|
def validate_or_copy_stim_files(self):
|
|
157
155
|
for vc_copy in self.stim_root_on_local.iterdir():
|
|
158
156
|
stim_copy = self.stim_root_on_stim / vc_copy.name
|
|
159
157
|
validate_or_overwrite(validate=stim_copy, src=vc_copy)
|
|
160
|
-
|
|
158
|
+
|
|
161
159
|
@property
|
|
162
|
-
def params(
|
|
160
|
+
def params(
|
|
161
|
+
self,
|
|
162
|
+
) -> dict[Literal["main", "mapping", "opto", "system"], dict[str, Any]]:
|
|
163
163
|
params = copy.deepcopy(DEFAULT_STIM_PARAMS)
|
|
164
164
|
params["mouse_id"] = str(self.mouse)
|
|
165
165
|
params["user_id"] = str(self.user)
|
|
@@ -167,26 +167,27 @@ class TTNMixin:
|
|
|
167
167
|
params["system"] = system
|
|
168
168
|
params["main"] = per_session_main_stim_params(self.ttn_session)
|
|
169
169
|
params["mapping"] = per_session_mapping_params(self.ttn_session)
|
|
170
|
-
params["opto"] =
|
|
170
|
+
params["opto"] = per_session_opto_params(self.ttn_session, self.mouse)
|
|
171
171
|
return params
|
|
172
172
|
|
|
173
173
|
@functools.cached_property
|
|
174
174
|
def scripts(self) -> dict[Literal["main", "mapping", "opto"], str]:
|
|
175
175
|
"""Local path on Stim computer to each script.
|
|
176
|
-
|
|
176
|
+
|
|
177
177
|
Verifies Stim copy matches v.c., or overwrites on Stim.
|
|
178
178
|
"""
|
|
179
179
|
for label in ("main", "mapping", "opto"):
|
|
180
180
|
script = self.script_names[label]
|
|
181
181
|
vc_copy = self.script_root_on_local / script
|
|
182
182
|
stim_copy = np_config.local_to_unc(
|
|
183
|
-
self.rig.stim,
|
|
183
|
+
self.rig.stim,
|
|
184
|
+
self.script_root_on_stim / script,
|
|
184
185
|
)
|
|
185
|
-
|
|
186
|
+
|
|
186
187
|
validate_or_overwrite(validate=stim_copy, src=vc_copy)
|
|
187
|
-
|
|
188
|
+
|
|
188
189
|
return {
|
|
189
|
-
label: str(self.script_root_on_stim / script)
|
|
190
|
+
label: str(self.script_root_on_stim / script)
|
|
190
191
|
for label, script in self.script_names.items()
|
|
191
192
|
}
|
|
192
193
|
|
|
@@ -194,8 +195,8 @@ class TTNMixin:
|
|
|
194
195
|
def system_camstim_params(self) -> dict[str, Any]:
|
|
195
196
|
"System config on Stim computer, if accessible."
|
|
196
197
|
return camstim_defaults()
|
|
197
|
-
|
|
198
|
-
|
|
198
|
+
|
|
199
|
+
|
|
199
200
|
class Hab(TTNMixin, np_workflows.PipelineHab):
|
|
200
201
|
def __init__(self, *args, **kwargs):
|
|
201
202
|
self.services = (
|
|
@@ -240,24 +241,28 @@ def new_experiment(
|
|
|
240
241
|
case _:
|
|
241
242
|
raise ValueError(f"Invalid session type: {session}")
|
|
242
243
|
experiment.ttn_session = session
|
|
243
|
-
|
|
244
|
+
|
|
244
245
|
with contextlib.suppress(Exception):
|
|
245
|
-
np_logging.web(f
|
|
246
|
-
|
|
246
|
+
np_logging.web(f"ttn_{experiment.ttn_session.name.lower()}").info(
|
|
247
|
+
f"{experiment} created"
|
|
248
|
+
)
|
|
249
|
+
|
|
247
250
|
return experiment
|
|
248
251
|
|
|
249
252
|
|
|
250
253
|
# --------------------------------------------------------------------------------------
|
|
251
254
|
|
|
255
|
+
|
|
252
256
|
def validate_or_overwrite(validate: str | pathlib.Path, src: str | pathlib.Path):
|
|
253
257
|
"Checksum validate against `src`, (over)write `validate` as `src` if different."
|
|
254
258
|
validate, src = pathlib.Path(validate), pathlib.Path(src)
|
|
259
|
+
|
|
255
260
|
def copy():
|
|
256
261
|
logger.debug("Copying %s to %s", src, validate)
|
|
257
262
|
shutil.copy2(src, validate)
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
263
|
+
|
|
264
|
+
while validate.exists() == False or (v := zlib.crc32(validate.read_bytes())) != (
|
|
265
|
+
c := zlib.crc32(pathlib.Path(src).read_bytes())
|
|
266
|
+
):
|
|
262
267
|
copy()
|
|
263
|
-
logger.debug("Validated %s CRC32: %08X", validate, (v & 0xFFFFFFFF)
|
|
268
|
+
logger.debug("Validated %s CRC32: %08X", validate, (v & 0xFFFFFFFF))
|
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
import configparser
|
|
2
|
-
import contextlib
|
|
3
|
-
import copy
|
|
4
|
-
import enum
|
|
5
|
-
import functools
|
|
6
|
-
from typing import ClassVar, Literal, NamedTuple, NoReturn, Optional, TypedDict
|
|
7
|
-
|
|
8
1
|
import IPython.display
|
|
9
2
|
import ipywidgets as ipw
|
|
10
|
-
import np_config
|
|
11
|
-
import np_logging
|
|
12
3
|
import np_session
|
|
13
|
-
import np_workflows
|
|
14
|
-
from pyparsing import Any
|
|
15
4
|
|
|
16
5
|
from .ttn_stim_config import TTNSession
|
|
17
6
|
|
|
@@ -20,6 +9,7 @@ global_state = {}
|
|
|
20
9
|
|
|
21
10
|
# for widget, before creating a experiment --------------------------------------------- #
|
|
22
11
|
|
|
12
|
+
|
|
23
13
|
class TTNSelectedSession:
|
|
24
14
|
def __init__(self, session: str | TTNSession, mouse: str | int | np_session.Mouse):
|
|
25
15
|
if isinstance(session, str):
|
|
@@ -48,17 +38,19 @@ def stim_session_select_widget(
|
|
|
48
38
|
options=tuple(_.value for _ in TTNSession),
|
|
49
39
|
description="Session",
|
|
50
40
|
)
|
|
51
|
-
|
|
41
|
+
|
|
52
42
|
def update_selection():
|
|
53
43
|
selection.__init__(str(session_dropdown.value), str(mouse))
|
|
54
|
-
|
|
55
|
-
if
|
|
44
|
+
|
|
45
|
+
if previously_selected_value := global_state.get("selected_session"):
|
|
56
46
|
session_dropdown.value = previously_selected_value
|
|
57
47
|
update_selection()
|
|
58
|
-
|
|
48
|
+
|
|
59
49
|
console = ipw.Output()
|
|
60
50
|
with console:
|
|
61
|
-
if last_session := np_session.Mouse(selection.mouse).state.get(
|
|
51
|
+
if last_session := np_session.Mouse(selection.mouse).state.get(
|
|
52
|
+
"last_ttn_session"
|
|
53
|
+
):
|
|
62
54
|
print(f"{mouse} last session: {last_session}")
|
|
63
55
|
print(f"Selected: {selection.session}")
|
|
64
56
|
|
|
@@ -74,9 +66,9 @@ def stim_session_select_widget(
|
|
|
74
66
|
update_selection()
|
|
75
67
|
with console:
|
|
76
68
|
print(f"Selected: {selection.session}")
|
|
77
|
-
global_state[
|
|
78
|
-
|
|
79
|
-
session_dropdown.observe(update, names=
|
|
69
|
+
global_state["selected_session"] = selection.session.value
|
|
70
|
+
|
|
71
|
+
session_dropdown.observe(update, names="value")
|
|
80
72
|
|
|
81
73
|
IPython.display.display(ipw.VBox([session_dropdown, console]))
|
|
82
74
|
|