np-services 0.1.62__py3-none-any.whl → 0.1.73__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_services/__init__.py +8 -8
- np_services/open_ephys.py +377 -378
- np_services/protocols.py +185 -185
- np_services/proxies.py +1489 -1488
- np_services/resources/mvr_connector.py +260 -260
- np_services/resources/zro.py +325 -325
- np_services/scripts/pretest.py +412 -328
- np_services/stim_computer_theme_changer.py +41 -41
- np_services/utils.py +167 -167
- {np_services-0.1.62.dist-info → np_services-0.1.73.dist-info}/METADATA +6 -7
- np_services-0.1.73.dist-info/RECORD +15 -0
- {np_services-0.1.62.dist-info → np_services-0.1.73.dist-info}/WHEEL +2 -1
- {np_services-0.1.62.dist-info → np_services-0.1.73.dist-info}/entry_points.txt +1 -1
- np_services-0.1.73.dist-info/top_level.txt +1 -0
- np_services/.mypy_cache/.gitignore +0 -2
- np_services/.mypy_cache/3.9/@plugins_snapshot.json +0 -1
- np_services/.mypy_cache/3.9/__future__.data.json +0 -1
- np_services/.mypy_cache/3.9/__future__.meta.json +0 -1
- np_services/.mypy_cache/3.9/_ast.data.json +0 -1
- np_services/.mypy_cache/3.9/_ast.meta.json +0 -1
- np_services/.mypy_cache/3.9/_codecs.data.json +0 -1
- np_services/.mypy_cache/3.9/_codecs.meta.json +0 -1
- np_services/.mypy_cache/3.9/_collections_abc.data.json +0 -1
- np_services/.mypy_cache/3.9/_collections_abc.meta.json +0 -1
- np_services/.mypy_cache/3.9/_ctypes.data.json +0 -1
- np_services/.mypy_cache/3.9/_ctypes.meta.json +0 -1
- np_services/.mypy_cache/3.9/_decimal.data.json +0 -1
- np_services/.mypy_cache/3.9/_decimal.meta.json +0 -1
- np_services/.mypy_cache/3.9/_random.data.json +0 -1
- np_services/.mypy_cache/3.9/_random.meta.json +0 -1
- np_services/.mypy_cache/3.9/_socket.data.json +0 -1
- np_services/.mypy_cache/3.9/_socket.meta.json +0 -1
- np_services/.mypy_cache/3.9/_thread.data.json +0 -1
- np_services/.mypy_cache/3.9/_thread.meta.json +0 -1
- np_services/.mypy_cache/3.9/_typeshed/__init__.data.json +0 -1
- np_services/.mypy_cache/3.9/_typeshed/__init__.meta.json +0 -1
- np_services/.mypy_cache/3.9/_warnings.data.json +0 -1
- np_services/.mypy_cache/3.9/_warnings.meta.json +0 -1
- np_services/.mypy_cache/3.9/_weakref.data.json +0 -1
- np_services/.mypy_cache/3.9/_weakref.meta.json +0 -1
- np_services/.mypy_cache/3.9/_weakrefset.data.json +0 -1
- np_services/.mypy_cache/3.9/_weakrefset.meta.json +0 -1
- np_services/.mypy_cache/3.9/_winapi.data.json +0 -1
- np_services/.mypy_cache/3.9/_winapi.meta.json +0 -1
- np_services/.mypy_cache/3.9/abc.data.json +0 -1
- np_services/.mypy_cache/3.9/abc.meta.json +0 -1
- np_services/.mypy_cache/3.9/array.data.json +0 -1
- np_services/.mypy_cache/3.9/array.meta.json +0 -1
- np_services/.mypy_cache/3.9/atexit.data.json +0 -1
- np_services/.mypy_cache/3.9/atexit.meta.json +0 -1
- np_services/.mypy_cache/3.9/builtins.data.json +0 -1
- np_services/.mypy_cache/3.9/builtins.meta.json +0 -1
- np_services/.mypy_cache/3.9/codecs.data.json +0 -1
- np_services/.mypy_cache/3.9/codecs.meta.json +0 -1
- np_services/.mypy_cache/3.9/collections/__init__.data.json +0 -1
- np_services/.mypy_cache/3.9/collections/__init__.meta.json +0 -1
- np_services/.mypy_cache/3.9/collections/abc.data.json +0 -1
- np_services/.mypy_cache/3.9/collections/abc.meta.json +0 -1
- np_services/.mypy_cache/3.9/contextlib.data.json +0 -1
- np_services/.mypy_cache/3.9/contextlib.meta.json +0 -1
- np_services/.mypy_cache/3.9/ctypes/__init__.data.json +0 -1
- np_services/.mypy_cache/3.9/ctypes/__init__.meta.json +0 -1
- np_services/.mypy_cache/3.9/datetime.data.json +0 -1
- np_services/.mypy_cache/3.9/datetime.meta.json +0 -1
- np_services/.mypy_cache/3.9/decimal.data.json +0 -1
- np_services/.mypy_cache/3.9/decimal.meta.json +0 -1
- np_services/.mypy_cache/3.9/email/__init__.data.json +0 -1
- np_services/.mypy_cache/3.9/email/__init__.meta.json +0 -1
- np_services/.mypy_cache/3.9/email/charset.data.json +0 -1
- np_services/.mypy_cache/3.9/email/charset.meta.json +0 -1
- np_services/.mypy_cache/3.9/email/contentmanager.data.json +0 -1
- np_services/.mypy_cache/3.9/email/contentmanager.meta.json +0 -1
- np_services/.mypy_cache/3.9/email/errors.data.json +0 -1
- np_services/.mypy_cache/3.9/email/errors.meta.json +0 -1
- np_services/.mypy_cache/3.9/email/header.data.json +0 -1
- np_services/.mypy_cache/3.9/email/header.meta.json +0 -1
- np_services/.mypy_cache/3.9/email/message.data.json +0 -1
- np_services/.mypy_cache/3.9/email/message.meta.json +0 -1
- np_services/.mypy_cache/3.9/email/policy.data.json +0 -1
- np_services/.mypy_cache/3.9/email/policy.meta.json +0 -1
- np_services/.mypy_cache/3.9/enum.data.json +0 -1
- np_services/.mypy_cache/3.9/enum.meta.json +0 -1
- np_services/.mypy_cache/3.9/errno.data.json +0 -1
- np_services/.mypy_cache/3.9/errno.meta.json +0 -1
- np_services/.mypy_cache/3.9/fractions.data.json +0 -1
- np_services/.mypy_cache/3.9/fractions.meta.json +0 -1
- np_services/.mypy_cache/3.9/genericpath.data.json +0 -1
- np_services/.mypy_cache/3.9/genericpath.meta.json +0 -1
- np_services/.mypy_cache/3.9/importlib/__init__.data.json +0 -1
- np_services/.mypy_cache/3.9/importlib/__init__.meta.json +0 -1
- np_services/.mypy_cache/3.9/importlib/abc.data.json +0 -1
- np_services/.mypy_cache/3.9/importlib/abc.meta.json +0 -1
- np_services/.mypy_cache/3.9/importlib/machinery.data.json +0 -1
- np_services/.mypy_cache/3.9/importlib/machinery.meta.json +0 -1
- np_services/.mypy_cache/3.9/importlib/metadata/__init__.data.json +0 -1
- np_services/.mypy_cache/3.9/importlib/metadata/__init__.meta.json +0 -1
- np_services/.mypy_cache/3.9/io.data.json +0 -1
- np_services/.mypy_cache/3.9/io.meta.json +0 -1
- np_services/.mypy_cache/3.9/json/__init__.data.json +0 -1
- np_services/.mypy_cache/3.9/json/__init__.meta.json +0 -1
- np_services/.mypy_cache/3.9/json/decoder.data.json +0 -1
- np_services/.mypy_cache/3.9/json/decoder.meta.json +0 -1
- np_services/.mypy_cache/3.9/json/encoder.data.json +0 -1
- np_services/.mypy_cache/3.9/json/encoder.meta.json +0 -1
- np_services/.mypy_cache/3.9/logging/__init__.data.json +0 -1
- np_services/.mypy_cache/3.9/logging/__init__.meta.json +0 -1
- np_services/.mypy_cache/3.9/math.data.json +0 -1
- np_services/.mypy_cache/3.9/math.meta.json +0 -1
- np_services/.mypy_cache/3.9/mmap.data.json +0 -1
- np_services/.mypy_cache/3.9/mmap.meta.json +0 -1
- np_services/.mypy_cache/3.9/np_services/__init__.data.json +0 -1
- np_services/.mypy_cache/3.9/np_services/__init__.meta.json +0 -1
- np_services/.mypy_cache/3.9/np_services/config.data.json +0 -1
- np_services/.mypy_cache/3.9/np_services/config.meta.json +0 -1
- np_services/.mypy_cache/3.9/np_services/protocols.data.json +0 -1
- np_services/.mypy_cache/3.9/np_services/protocols.meta.json +0 -1
- np_services/.mypy_cache/3.9/np_services/zro.data.json +0 -1
- np_services/.mypy_cache/3.9/np_services/zro.meta.json +0 -1
- np_services/.mypy_cache/3.9/ntpath.data.json +0 -1
- np_services/.mypy_cache/3.9/ntpath.meta.json +0 -1
- np_services/.mypy_cache/3.9/numbers.data.json +0 -1
- np_services/.mypy_cache/3.9/numbers.meta.json +0 -1
- np_services/.mypy_cache/3.9/os/__init__.data.json +0 -1
- np_services/.mypy_cache/3.9/os/__init__.meta.json +0 -1
- np_services/.mypy_cache/3.9/os/path.data.json +0 -1
- np_services/.mypy_cache/3.9/os/path.meta.json +0 -1
- np_services/.mypy_cache/3.9/pathlib.data.json +0 -1
- np_services/.mypy_cache/3.9/pathlib.meta.json +0 -1
- np_services/.mypy_cache/3.9/pickle.data.json +0 -1
- np_services/.mypy_cache/3.9/pickle.meta.json +0 -1
- np_services/.mypy_cache/3.9/platform.data.json +0 -1
- np_services/.mypy_cache/3.9/platform.meta.json +0 -1
- np_services/.mypy_cache/3.9/posixpath.data.json +0 -1
- np_services/.mypy_cache/3.9/posixpath.meta.json +0 -1
- np_services/.mypy_cache/3.9/random.data.json +0 -1
- np_services/.mypy_cache/3.9/random.meta.json +0 -1
- np_services/.mypy_cache/3.9/re.data.json +0 -1
- np_services/.mypy_cache/3.9/re.meta.json +0 -1
- np_services/.mypy_cache/3.9/shutil.data.json +0 -1
- np_services/.mypy_cache/3.9/shutil.meta.json +0 -1
- np_services/.mypy_cache/3.9/socket.data.json +0 -1
- np_services/.mypy_cache/3.9/socket.meta.json +0 -1
- np_services/.mypy_cache/3.9/sre_compile.data.json +0 -1
- np_services/.mypy_cache/3.9/sre_compile.meta.json +0 -1
- np_services/.mypy_cache/3.9/sre_constants.data.json +0 -1
- np_services/.mypy_cache/3.9/sre_constants.meta.json +0 -1
- np_services/.mypy_cache/3.9/sre_parse.data.json +0 -1
- np_services/.mypy_cache/3.9/sre_parse.meta.json +0 -1
- np_services/.mypy_cache/3.9/string.data.json +0 -1
- np_services/.mypy_cache/3.9/string.meta.json +0 -1
- np_services/.mypy_cache/3.9/subprocess.data.json +0 -1
- np_services/.mypy_cache/3.9/subprocess.meta.json +0 -1
- np_services/.mypy_cache/3.9/sys.data.json +0 -1
- np_services/.mypy_cache/3.9/sys.meta.json +0 -1
- np_services/.mypy_cache/3.9/threading.data.json +0 -1
- np_services/.mypy_cache/3.9/threading.meta.json +0 -1
- np_services/.mypy_cache/3.9/time.data.json +0 -1
- np_services/.mypy_cache/3.9/time.meta.json +0 -1
- np_services/.mypy_cache/3.9/types.data.json +0 -1
- np_services/.mypy_cache/3.9/types.meta.json +0 -1
- np_services/.mypy_cache/3.9/typing.data.json +0 -1
- np_services/.mypy_cache/3.9/typing.meta.json +0 -1
- np_services/.mypy_cache/3.9/typing_extensions.data.json +0 -1
- np_services/.mypy_cache/3.9/typing_extensions.meta.json +0 -1
- np_services/.mypy_cache/3.9/warnings.data.json +0 -1
- np_services/.mypy_cache/3.9/warnings.meta.json +0 -1
- np_services/.mypy_cache/3.9/weakref.data.json +0 -1
- np_services/.mypy_cache/3.9/weakref.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/__init__.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/__init__.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/_typing.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/_typing.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/backend/__init__.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/backend/__init__.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/backend/select.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/backend/select.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/constants.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/constants.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/error.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/error.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/__init__.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/__init__.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/attrsettr.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/attrsettr.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/context.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/context.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/frame.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/frame.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/poll.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/poll.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/socket.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/socket.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/tracker.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/tracker.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/version.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/sugar/version.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/utils/__init__.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/utils/__init__.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/utils/interop.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/utils/interop.meta.json +0 -1
- np_services/.mypy_cache/3.9/zmq/utils/jsonapi.data.json +0 -1
- np_services/.mypy_cache/3.9/zmq/utils/jsonapi.meta.json +0 -1
- np_services/.mypy_cache/CACHEDIR.TAG +0 -3
- np_services/resources/black_desktop.ps1 +0 -66
- np_services/resources/grey_desktop.ps1 +0 -66
- np_services/resources/reset_desktop.ps1 +0 -66
- np_services-0.1.62.dist-info/RECORD +0 -206
np_services/open_ephys.py
CHANGED
|
@@ -1,378 +1,377 @@
|
|
|
1
|
-
import enum
|
|
2
|
-
import json
|
|
3
|
-
import os
|
|
4
|
-
import pathlib
|
|
5
|
-
import shutil
|
|
6
|
-
import socket
|
|
7
|
-
import sys
|
|
8
|
-
import time
|
|
9
|
-
from typing import Any, Optional, Sequence
|
|
10
|
-
|
|
11
|
-
import np_config
|
|
12
|
-
import np_logging
|
|
13
|
-
import requests
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
global
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
logger.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
recording["
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
)
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
time.sleep(0.5)
|
|
274
|
-
|
|
275
|
-
time.sleep(0.5)
|
|
276
|
-
|
|
277
|
-
time.sleep(0.5)
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
sub
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
)
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
)
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
logger.
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
)
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
#
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
#
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
)
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
#
|
|
360
|
-
#
|
|
361
|
-
#
|
|
362
|
-
#
|
|
363
|
-
#
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
""
|
|
367
|
-
|
|
368
|
-
r
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
r
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
r
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
"""
|
|
1
|
+
import enum
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import pathlib
|
|
5
|
+
import shutil
|
|
6
|
+
import socket
|
|
7
|
+
import sys
|
|
8
|
+
import time
|
|
9
|
+
from typing import Any, Optional, Sequence
|
|
10
|
+
|
|
11
|
+
import np_config
|
|
12
|
+
import np_logging
|
|
13
|
+
import requests
|
|
14
|
+
|
|
15
|
+
import np_services.utils as utils
|
|
16
|
+
from np_services.protocols import TestError
|
|
17
|
+
|
|
18
|
+
# global vars -------------------------------------------------------------------------- #
|
|
19
|
+
logger = np_logging.getLogger(__name__) # logs will show full module path
|
|
20
|
+
|
|
21
|
+
__name = "OpenEphys" # Service protocol operations will see just the 'class' name
|
|
22
|
+
|
|
23
|
+
exc: Optional[BaseException] = None
|
|
24
|
+
initialized: float = 0
|
|
25
|
+
"`time.time()` when the service was initialized."
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
host: str = np_config.Rig().Acq
|
|
29
|
+
except ValueError:
|
|
30
|
+
logger.warning("Not connected to a rig: `OpenEphys.host` needs to be set manually.")
|
|
31
|
+
port: str | int = 37497 # 1-800-EPHYS
|
|
32
|
+
latest_start: float = 0
|
|
33
|
+
"`time.time()` when the service was last started via `start()`."
|
|
34
|
+
|
|
35
|
+
# for launching:
|
|
36
|
+
rsc_app_id = "open-ephys"
|
|
37
|
+
|
|
38
|
+
# device records:
|
|
39
|
+
gb_per_hr: int | float = 250 # per drive
|
|
40
|
+
min_rec_hr: int | float = 2
|
|
41
|
+
pretest_duration_sec: int | float = 0.5
|
|
42
|
+
|
|
43
|
+
# for resulting data:
|
|
44
|
+
folder: str #! required
|
|
45
|
+
"The string that will be sent to Open Ephys to name the recording: typically `0123456789_366122_20220618`"
|
|
46
|
+
data_files: list[pathlib.Path] = []
|
|
47
|
+
"Storage for paths collected over the experiment."
|
|
48
|
+
data_root: Optional[pathlib.Path] = None
|
|
49
|
+
|
|
50
|
+
# for validation
|
|
51
|
+
sync_path: Optional[pathlib.Path] = None
|
|
52
|
+
|
|
53
|
+
# -------------------------------------------------------------------------------------- #
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class State(enum.Enum):
|
|
57
|
+
idle = "IDLE"
|
|
58
|
+
acquire = "ACQUIRE"
|
|
59
|
+
record = "RECORD"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class Endpoint(enum.Enum):
|
|
63
|
+
status = "status"
|
|
64
|
+
recording = "recording"
|
|
65
|
+
processors = "processors"
|
|
66
|
+
message = "message"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def launch() -> None:
|
|
70
|
+
utils.start_rsc_app(host, rsc_app_id)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def pretest() -> None:
|
|
74
|
+
logger.info("OpenEphys | Starting pretest")
|
|
75
|
+
global folder
|
|
76
|
+
folder = "_pretest_"
|
|
77
|
+
initialize()
|
|
78
|
+
test()
|
|
79
|
+
try:
|
|
80
|
+
start()
|
|
81
|
+
time.sleep(pretest_duration_sec)
|
|
82
|
+
verify()
|
|
83
|
+
finally:
|
|
84
|
+
stop()
|
|
85
|
+
finalize()
|
|
86
|
+
validate()
|
|
87
|
+
logger.info("OpenEphys | Pretest passed")
|
|
88
|
+
|
|
89
|
+
def url(endpoint: Endpoint):
|
|
90
|
+
return f"http://{host}:{port}/api/{endpoint.value}"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def get_state() -> requests.Response:
|
|
94
|
+
mode = requests.get(u := url(Endpoint.status)).json().get("mode")
|
|
95
|
+
logger.debug("%s -> get mode: %s", u, mode)
|
|
96
|
+
return mode
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def set_state(state: State) -> requests.Response:
|
|
100
|
+
msg = {"mode": state.value}
|
|
101
|
+
mode = requests.put(u := url(Endpoint.status), json.dumps(msg))
|
|
102
|
+
logger.debug("%s <- set mode: %s", u, state.value)
|
|
103
|
+
return mode
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def is_connected() -> bool:
|
|
107
|
+
global exc
|
|
108
|
+
|
|
109
|
+
if not utils.is_online(host):
|
|
110
|
+
exc = TestError(
|
|
111
|
+
f"OpenEphys | No response from {host}: may be offline or unreachable"
|
|
112
|
+
)
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
state = get_state()
|
|
117
|
+
except requests.RequestException:
|
|
118
|
+
exc = TestError(
|
|
119
|
+
f"OpenEphys | No response from Open Ephys http server: is the software started?"
|
|
120
|
+
)
|
|
121
|
+
return False
|
|
122
|
+
else:
|
|
123
|
+
if not any(_.value == state for _ in State):
|
|
124
|
+
exc = TestError(f"OpenEphys | Unexpected state: {state}")
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
return True
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def initialize() -> None:
|
|
131
|
+
logger.info("OpenEphys | Initializing")
|
|
132
|
+
global data_files
|
|
133
|
+
data_files = []
|
|
134
|
+
global initialized
|
|
135
|
+
initialized = time.time()
|
|
136
|
+
global folder
|
|
137
|
+
set_folder(folder)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def get_required_disk_gb() -> int | float:
|
|
141
|
+
"""The minimum amount of free disk space required to start recording."""
|
|
142
|
+
return gb_per_hr * min_rec_hr
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def is_disk_space_ok() -> bool:
|
|
146
|
+
global exc
|
|
147
|
+
exc = None
|
|
148
|
+
required = get_required_disk_gb()
|
|
149
|
+
for data_root in get_data_roots():
|
|
150
|
+
try:
|
|
151
|
+
free = utils.free_gb(data_root)
|
|
152
|
+
except FileNotFoundError as e:
|
|
153
|
+
exc = e
|
|
154
|
+
logger.exception(f"{__name} data path not accessible: {data_root}")
|
|
155
|
+
else:
|
|
156
|
+
logger.info(
|
|
157
|
+
"%s free disk space on %s: %s GB", __name, data_root.drive, free
|
|
158
|
+
)
|
|
159
|
+
if free < required:
|
|
160
|
+
exc = ValueError(
|
|
161
|
+
f"{__name} free disk space on {data_root.drive} doesn't meet minimum of {required} GB"
|
|
162
|
+
)
|
|
163
|
+
if exc:
|
|
164
|
+
return False
|
|
165
|
+
return True
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test() -> None:
|
|
169
|
+
logger.info("OpenEphys | Testing")
|
|
170
|
+
if not is_connected():
|
|
171
|
+
if exc:
|
|
172
|
+
raise TestError(f"Acq computer {host} isn't responding, or OpenEphys isn't open") from exc
|
|
173
|
+
gb = get_required_disk_gb()
|
|
174
|
+
if not is_disk_space_ok():
|
|
175
|
+
raise TestError(
|
|
176
|
+
f"{__name} free disk space on one or more recording drives doesn't meet minimum of {gb} GB"
|
|
177
|
+
) from exc
|
|
178
|
+
unlock_previous_recording()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def is_started() -> bool:
|
|
182
|
+
if get_state() == State.record.value:
|
|
183
|
+
return True
|
|
184
|
+
return False
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def is_ready_to_start() -> bool:
|
|
188
|
+
if get_state() == State.acquire.value:
|
|
189
|
+
return True
|
|
190
|
+
return False
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def start() -> None:
|
|
194
|
+
logger.info("OpenEphys | Starting recording")
|
|
195
|
+
if is_started():
|
|
196
|
+
logger.warning("OpenEphys is already started")
|
|
197
|
+
return
|
|
198
|
+
if not is_ready_to_start():
|
|
199
|
+
set_state(State.acquire)
|
|
200
|
+
time.sleep(0.5)
|
|
201
|
+
global latest_start
|
|
202
|
+
latest_start = time.time()
|
|
203
|
+
set_state(State.record)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def stop() -> None:
|
|
207
|
+
logger.info("OpenEphys | Stopping recording")
|
|
208
|
+
set_state(State.acquire)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def finalize() -> None:
|
|
212
|
+
logger.info("OpenEphys | Finalizing")
|
|
213
|
+
data_files.extend(get_latest_data_dirs())
|
|
214
|
+
unlock_previous_recording()
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def set_folder(
|
|
218
|
+
name: str, prepend_text: str = "", append_text: str = ""
|
|
219
|
+
) -> None:
|
|
220
|
+
"""Recording folder string"""
|
|
221
|
+
recording = requests.get(url(Endpoint.recording)).json()
|
|
222
|
+
|
|
223
|
+
if name == "":
|
|
224
|
+
name = "_"
|
|
225
|
+
logger.warning(
|
|
226
|
+
"OpenEphys | Recording directory cannot be empty, replaced with underscore: %s",
|
|
227
|
+
name,
|
|
228
|
+
)
|
|
229
|
+
if "." in name:
|
|
230
|
+
name.replace(".", "_")
|
|
231
|
+
logger.warning(
|
|
232
|
+
"OpenEphys | Recording directory cannot contain periods, replaced with underscores: %s",
|
|
233
|
+
name,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
recording["base_text"] = name
|
|
237
|
+
recording["prepend_text"] = prepend_text
|
|
238
|
+
recording["append_text"] = append_text
|
|
239
|
+
logger.debug(
|
|
240
|
+
"OpenEphys | Setting recording directory to: %s",
|
|
241
|
+
prepend_text + name + append_text,
|
|
242
|
+
)
|
|
243
|
+
response = requests.put(url(Endpoint.recording), json.dumps(recording))
|
|
244
|
+
time.sleep(0.1)
|
|
245
|
+
if (actual := response.json().get("base_text")) != name:
|
|
246
|
+
raise TestError(
|
|
247
|
+
f"OpenEphys | Set folder to {name}, but software shows: {actual}"
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def get_folder() -> str | None:
|
|
252
|
+
return requests.get(url(Endpoint.recording)).json().get("base_text")
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def clear_open_ephys_name() -> None:
|
|
256
|
+
set_folder("_temp_")
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def set_idle():
|
|
260
|
+
"Should be called before sending any configuration to Open Ephys"
|
|
261
|
+
if is_started():
|
|
262
|
+
stop()
|
|
263
|
+
time.sleep(0.5)
|
|
264
|
+
set_state(State.idle)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def unlock_previous_recording():
|
|
268
|
+
"stop rec/acquiring | set name to _temp_ | record briefly | acquire | set name to folder"
|
|
269
|
+
logger.debug("OpenEphys | Unlocking previous recording")
|
|
270
|
+
set_idle()
|
|
271
|
+
time.sleep(0.5)
|
|
272
|
+
clear_open_ephys_name()
|
|
273
|
+
time.sleep(0.5)
|
|
274
|
+
start()
|
|
275
|
+
time.sleep(0.5)
|
|
276
|
+
stop()
|
|
277
|
+
time.sleep(0.5)
|
|
278
|
+
global folder
|
|
279
|
+
set_folder(folder)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def get_record_nodes() -> list[dict[str, Any]]:
|
|
283
|
+
"""Returns a list of record node info dicts, incl keys `node_id`, `parent_directory`"""
|
|
284
|
+
return requests.get(url(Endpoint.recording)).json().get("record_nodes", None) or []
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def get_data_roots() -> list[pathlib.Path]:
|
|
288
|
+
return [
|
|
289
|
+
pathlib.Path(f"//{host}/{_['parent_directory'].replace(':','')}")
|
|
290
|
+
for _ in get_record_nodes()
|
|
291
|
+
]
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def get_latest_data_dirs() -> list[pathlib.Path]:
|
|
295
|
+
"""Returns the path to the latest data folder, based on the latest modified time"""
|
|
296
|
+
dirs = []
|
|
297
|
+
for root in get_data_roots():
|
|
298
|
+
if subfolders := [
|
|
299
|
+
sub
|
|
300
|
+
for sub in root.iterdir()
|
|
301
|
+
if sub.is_dir()
|
|
302
|
+
and not any(
|
|
303
|
+
_ in str(sub) for _ in ["System Volume Information", "$RECYCLE.BIN"]
|
|
304
|
+
)
|
|
305
|
+
]:
|
|
306
|
+
subfolders.sort(key=lambda _: _.stat().st_ctime)
|
|
307
|
+
dirs.append(subfolders[-1])
|
|
308
|
+
return dirs
|
|
309
|
+
|
|
310
|
+
def check_files_increasing_in_size() -> None:
|
|
311
|
+
for data_dir in get_latest_data_dirs():
|
|
312
|
+
for file in reversed(
|
|
313
|
+
utils.get_files_created_between(
|
|
314
|
+
data_dir, "*/*/*/continuous/*/sample_numbers.npy", latest_start
|
|
315
|
+
)
|
|
316
|
+
):
|
|
317
|
+
if utils.is_file_growing(file):
|
|
318
|
+
break
|
|
319
|
+
else:
|
|
320
|
+
raise TestError(
|
|
321
|
+
f"OpenEphys | Data file(s) not increasing in size in {data_dir}"
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
def verify() -> None:
|
|
325
|
+
logger.debug("OpenEphys | Verifying")
|
|
326
|
+
check_files_increasing_in_size()
|
|
327
|
+
logger.info(
|
|
328
|
+
"OpenEphys | Verified files are increasing in size for all Record Nodes"
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def validate() -> None:
|
|
333
|
+
logger.info(f"OpenEphys | Validating")
|
|
334
|
+
import npc_ephys
|
|
335
|
+
npc_ephys.validate_ephys(
|
|
336
|
+
root_paths=data_files,
|
|
337
|
+
sync_path_or_dataset=sync_path,
|
|
338
|
+
ignore_small_folders=True,
|
|
339
|
+
)
|
|
340
|
+
logger.info(f"OpenEphys | Validated data {'with' if sync_path else 'without'} sync")
|
|
341
|
+
|
|
342
|
+
def set_ref(ext_tip="TIP"):
|
|
343
|
+
# for port in [0, 1, 2]:
|
|
344
|
+
# for slot in [0, 1, 2]:
|
|
345
|
+
|
|
346
|
+
slot = 2 #! Test
|
|
347
|
+
port = 1 #! Test
|
|
348
|
+
dock = 1 # TODO may be 1 or 2 with firmware upgrade
|
|
349
|
+
tip_ref_msg = {"text": f"NP REFERENCE {slot} {port} {dock} {ext_tip}"}
|
|
350
|
+
# logger.info(f"sending ...
|
|
351
|
+
# return
|
|
352
|
+
requests.put(
|
|
353
|
+
"http://localhost:37497/api/processors/100/config", json.dumps(tip_ref_msg)
|
|
354
|
+
)
|
|
355
|
+
time.sleep(3)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
# TODO set up everything possible from here to avoid accidentally changed settings ?
|
|
359
|
+
# probe channels
|
|
360
|
+
# sampling rate
|
|
361
|
+
# tip ref
|
|
362
|
+
# signal chain?
|
|
363
|
+
# acq drive letters
|
|
364
|
+
|
|
365
|
+
"""
|
|
366
|
+
if __name__ == "__main__":
|
|
367
|
+
r = requests.get(Endpoint.recording)
|
|
368
|
+
print((r.json()['current_directory_name'], r.json()['prepend_text'], r.json()['append_text']))
|
|
369
|
+
|
|
370
|
+
r = EphysHTTP.set_folder(path = "mouseID_", prepend_text="sessionID", append_text="_date")
|
|
371
|
+
print((r.json()['current_directory_name'], r.json()['prepend_text'], r.json()['append_text']))
|
|
372
|
+
|
|
373
|
+
r = EphysHTTP.set_folder(path = "mouse", prepend_text="session", append_text="date")
|
|
374
|
+
print((r.json()['current_directory_name'], r.json()['prepend_text'], r.json()['append_text']))
|
|
375
|
+
|
|
376
|
+
print((r.json()['base_text'])) # fails as of 06/23 https://github.com/open-ephys/plugin-GUI/pull/514
|
|
377
|
+
"""
|