pychemstation 0.10.4__py3-none-any.whl → 0.10.6__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.
- pychemstation/analysis/__init__.py +8 -1
- pychemstation/analysis/chromatogram.py +20 -0
- pychemstation/analysis/process_report.py +125 -63
- pychemstation/control/__init__.py +2 -0
- pychemstation/control/controllers/__init__.py +2 -3
- pychemstation/control/controllers/abc_tables/device.py +15 -0
- pychemstation/control/controllers/abc_tables/run.py +228 -0
- pychemstation/control/controllers/abc_tables/table.py +221 -0
- pychemstation/control/controllers/comm.py +25 -106
- pychemstation/control/controllers/data_aq/__init__.py +4 -0
- pychemstation/control/controllers/{tables → data_aq}/method.py +52 -95
- pychemstation/control/controllers/{tables → data_aq}/sequence.py +199 -141
- pychemstation/control/controllers/devices/__init__.py +3 -0
- pychemstation/control/controllers/devices/injector.py +69 -24
- pychemstation/control/hplc.py +15 -17
- pychemstation/utils/injector_types.py +23 -3
- pychemstation/utils/macro.py +2 -2
- pychemstation/utils/method_types.py +1 -1
- pychemstation/utils/mocking/__init__.py +0 -0
- pychemstation/utils/mocking/abc_comm.py +160 -0
- pychemstation/utils/mocking/mock_comm.py +5 -0
- pychemstation/utils/mocking/mock_hplc.py +2 -0
- pychemstation/utils/sequence_types.py +19 -0
- pychemstation/utils/table_types.py +6 -0
- pychemstation/utils/tray_types.py +36 -1
- {pychemstation-0.10.4.dist-info → pychemstation-0.10.6.dist-info}/METADATA +4 -4
- pychemstation-0.10.6.dist-info/RECORD +42 -0
- pychemstation/control/controllers/devices/device.py +0 -49
- pychemstation/control/controllers/tables/ms.py +0 -24
- pychemstation/control/controllers/tables/table.py +0 -375
- pychemstation-0.10.4.dist-info/RECORD +0 -37
- /pychemstation/control/controllers/{tables → abc_tables}/__init__.py +0 -0
- {pychemstation-0.10.4.dist-info → pychemstation-0.10.6.dist-info}/WHEEL +0 -0
- {pychemstation-0.10.4.dist-info → pychemstation-0.10.6.dist-info}/licenses/LICENSE +0 -0
@@ -1,18 +1,19 @@
|
|
1
1
|
import os
|
2
2
|
import time
|
3
3
|
import warnings
|
4
|
-
from typing import Dict, List, Optional
|
4
|
+
from typing import Any, Dict, List, Optional, Union
|
5
5
|
|
6
6
|
from result import Err, Ok, Result
|
7
7
|
from typing_extensions import override
|
8
8
|
|
9
|
-
from ....analysis.process_report import AgilentReport, ReportType
|
10
|
-
from ....control.controllers.comm import CommunicationController
|
11
9
|
from pychemstation.analysis.chromatogram import (
|
12
10
|
SEQUENCE_TIME_FORMAT,
|
13
11
|
AgilentChannelChromatogramData,
|
14
12
|
AgilentHPLCChromatogram,
|
15
13
|
)
|
14
|
+
|
15
|
+
from ....analysis.process_report import AgilentReport, ReportType
|
16
|
+
from ....control.controllers.comm import CommunicationController
|
16
17
|
from ....utils.macro import Command
|
17
18
|
from ....utils.sequence_types import (
|
18
19
|
InjectionSource,
|
@@ -21,20 +22,20 @@ from ....utils.sequence_types import (
|
|
21
22
|
SequenceEntry,
|
22
23
|
SequenceTable,
|
23
24
|
)
|
24
|
-
from ....utils.table_types import RegisterFlag, Table
|
25
|
-
from ....utils.tray_types import FiftyFourVialPlate, TenVialColumn
|
26
|
-
from .. import
|
27
|
-
from .
|
25
|
+
from ....utils.table_types import RegisterFlag, T, Table
|
26
|
+
from ....utils.tray_types import FiftyFourVialPlate, TenVialColumn, Tray
|
27
|
+
from ..abc_tables.run import RunController
|
28
|
+
from . import MethodController
|
28
29
|
|
29
30
|
|
30
|
-
class SequenceController(
|
31
|
+
class SequenceController(RunController):
|
31
32
|
"""
|
32
33
|
Class containing sequence related logic
|
33
34
|
"""
|
34
35
|
|
35
36
|
def __init__(
|
36
37
|
self,
|
37
|
-
controller: CommunicationController,
|
38
|
+
controller: Optional[CommunicationController],
|
38
39
|
method_controller: MethodController,
|
39
40
|
src: str,
|
40
41
|
data_dirs: List[str],
|
@@ -57,7 +58,7 @@ class SequenceController(TableController):
|
|
57
58
|
seq_name = self.receive()
|
58
59
|
|
59
60
|
if rows.is_ok() and seq_name.is_ok():
|
60
|
-
self.table_state = SequenceTable(
|
61
|
+
self.table_state: SequenceTable = SequenceTable(
|
61
62
|
name=seq_name.ok_value.string_response.partition(".S")[0],
|
62
63
|
rows=[
|
63
64
|
self.get_row(r + 1) for r in range(int(rows.ok_value.num_response))
|
@@ -66,19 +67,37 @@ class SequenceController(TableController):
|
|
66
67
|
return self.table_state
|
67
68
|
raise RuntimeError(rows.err_value)
|
68
69
|
|
70
|
+
def try_int(self, val: Any) -> Optional[int]:
|
71
|
+
try:
|
72
|
+
return int(val)
|
73
|
+
except ValueError:
|
74
|
+
return None
|
75
|
+
|
76
|
+
def try_float(self, val: Any) -> Optional[float]:
|
77
|
+
try:
|
78
|
+
return float(val)
|
79
|
+
except ValueError:
|
80
|
+
return None
|
81
|
+
|
82
|
+
def try_vial_location(self, val: Any) -> Tray:
|
83
|
+
try:
|
84
|
+
return (
|
85
|
+
TenVialColumn(val)
|
86
|
+
if val <= 10
|
87
|
+
else FiftyFourVialPlate.from_int(num=val)
|
88
|
+
)
|
89
|
+
except ValueError:
|
90
|
+
raise ValueError("Expected vial location, is empty.")
|
91
|
+
|
69
92
|
def get_row(self, row: int) -> SequenceEntry:
|
70
93
|
sample_name = self.get_text(row, RegisterFlag.NAME)
|
71
|
-
vial_location =
|
94
|
+
vial_location = self.try_int(self.get_num(row, RegisterFlag.VIAL_LOCATION))
|
72
95
|
method = self.get_text(row, RegisterFlag.METHOD)
|
73
|
-
num_inj =
|
74
|
-
inj_vol =
|
96
|
+
num_inj = self.try_int(self.get_num(row, RegisterFlag.NUM_INJ))
|
97
|
+
inj_vol = self.try_float(self.get_text(row, RegisterFlag.INJ_VOL))
|
75
98
|
inj_source = InjectionSource(self.get_text(row, RegisterFlag.INJ_SOR))
|
76
99
|
sample_type = SampleType(self.get_num(row, RegisterFlag.SAMPLE_TYPE))
|
77
|
-
vial_enum = (
|
78
|
-
TenVialColumn(vial_location)
|
79
|
-
if vial_location <= 10
|
80
|
-
else FiftyFourVialPlate.from_int(num=vial_location)
|
81
|
-
)
|
100
|
+
vial_enum = self.try_vial_location(vial_location)
|
82
101
|
return SequenceEntry(
|
83
102
|
sample_name=sample_name,
|
84
103
|
vial_location=vial_enum,
|
@@ -113,7 +132,7 @@ class SequenceController(TableController):
|
|
113
132
|
parsed_response = self.receive().ok_value.string_response
|
114
133
|
|
115
134
|
assert parsed_response == f"{seq_name}.S", "Switching sequence failed."
|
116
|
-
self.table_state =
|
135
|
+
self.table_state = self.load()
|
117
136
|
|
118
137
|
def edit(self, sequence_table: SequenceTable):
|
119
138
|
"""
|
@@ -127,13 +146,12 @@ class SequenceController(TableController):
|
|
127
146
|
if rows.is_ok():
|
128
147
|
existing_row_num = rows.ok_value.num_response
|
129
148
|
wanted_row_num = len(sequence_table.rows)
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
149
|
+
for i in range(int(existing_row_num)):
|
150
|
+
self.delete_row(int(existing_row_num - i))
|
151
|
+
self.send(Command.SAVE_SEQUENCE_CMD)
|
152
|
+
for i in range(int(wanted_row_num)):
|
153
|
+
self.add_row()
|
135
154
|
self.send(Command.SAVE_SEQUENCE_CMD)
|
136
|
-
existing_row_num = self.get_num_rows().ok_value.num_response
|
137
155
|
self.send(Command.SWITCH_SEQUENCE_CMD)
|
138
156
|
|
139
157
|
for i, row in enumerate(sequence_table.rows):
|
@@ -156,87 +174,114 @@ class SequenceController(TableController):
|
|
156
174
|
self.send(Command.SAVE_SEQUENCE_CMD)
|
157
175
|
num_rows = self.get_num_rows()
|
158
176
|
if row.vial_location:
|
159
|
-
|
160
|
-
if isinstance(loc, TenVialColumn):
|
161
|
-
loc = row.vial_location.value
|
162
|
-
elif isinstance(loc, FiftyFourVialPlate):
|
163
|
-
loc = row.vial_location.value()
|
164
|
-
self._edit_row_num(
|
165
|
-
row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc
|
166
|
-
)
|
177
|
+
self.edit_vial_location(row.vial_location, row_num)
|
167
178
|
if row.method:
|
168
|
-
|
169
|
-
possible_path = os.path.join(method_dir, row.method) + ".M\\"
|
170
|
-
method = row.method
|
171
|
-
if os.path.exists(possible_path):
|
172
|
-
method = os.path.join(method_dir, row.method)
|
173
|
-
self._edit_row_text(row=row_num, col_name=RegisterFlag.METHOD, val=method)
|
179
|
+
self.edit_method_name(row.method, row_num)
|
174
180
|
if row.num_inj:
|
175
|
-
self.
|
176
|
-
row=row_num, col_name=RegisterFlag.NUM_INJ, val=row.num_inj
|
177
|
-
)
|
181
|
+
self.edit_num_injections(row.num_inj, row_num)
|
178
182
|
if row.inj_vol:
|
179
|
-
self.
|
180
|
-
row=row_num, col_name=RegisterFlag.INJ_VOL, val=row.inj_vol
|
181
|
-
)
|
183
|
+
self.edit_injection_volume(row.inj_vol, row_num)
|
182
184
|
if row.inj_source:
|
183
|
-
self.
|
184
|
-
row=row_num, col_name=RegisterFlag.INJ_SOR, val=row.inj_source.value
|
185
|
-
)
|
185
|
+
self.edit_injection_source(row.inj_source, row_num)
|
186
186
|
if row.sample_name:
|
187
|
-
self.
|
188
|
-
|
189
|
-
)
|
190
|
-
|
191
|
-
|
192
|
-
row=row_num, col_name=RegisterFlag.DATA_FILE, val=row.data_file
|
193
|
-
)
|
194
|
-
else:
|
195
|
-
self._edit_row_text(
|
196
|
-
row=row_num, col_name=RegisterFlag.DATA_FILE, val=row.sample_name
|
197
|
-
)
|
187
|
+
self.edit_sample_name(row.sample_name, row_num)
|
188
|
+
if row.data_file:
|
189
|
+
self.edit_data_file(row.data_file, row_num)
|
190
|
+
elif row.sample_name and not row.data_file:
|
191
|
+
self.edit_data_file(row.sample_name, row_num)
|
198
192
|
if row.sample_type:
|
199
|
-
self.
|
200
|
-
row=row_num,
|
201
|
-
col_name=RegisterFlag.SAMPLE_TYPE,
|
202
|
-
val=row.sample_type.value,
|
203
|
-
)
|
204
|
-
|
193
|
+
self.edit_sample_type(row.sample_type, row_num)
|
205
194
|
self.send(Command.SAVE_SEQUENCE_CMD)
|
206
195
|
|
196
|
+
def edit_sample_type(self, sample_type: SampleType, row_num: int):
|
197
|
+
self._edit_row_num(
|
198
|
+
row=row_num,
|
199
|
+
col_name=RegisterFlag.SAMPLE_TYPE,
|
200
|
+
val=sample_type.value,
|
201
|
+
)
|
202
|
+
|
203
|
+
def edit_data_file(self, data_file: str, row_num: int):
|
204
|
+
self._edit_row_text(row=row_num, col_name=RegisterFlag.DATA_FILE, val=data_file)
|
205
|
+
|
206
|
+
def edit_sample_name(self, sample_name: str, row_num: int):
|
207
|
+
self._edit_row_text(row=row_num, col_name=RegisterFlag.NAME, val=sample_name)
|
208
|
+
|
209
|
+
def edit_injection_source(self, inj_source: InjectionSource, row_num: int):
|
210
|
+
self._edit_row_text(
|
211
|
+
row=row_num, col_name=RegisterFlag.INJ_SOR, val=inj_source.value
|
212
|
+
)
|
213
|
+
|
214
|
+
def edit_injection_volume(self, inj_vol: Union[int, float], row_num: int):
|
215
|
+
self._edit_row_text(
|
216
|
+
row=row_num, col_name=RegisterFlag.INJ_VOL, val=str(inj_vol)
|
217
|
+
)
|
218
|
+
|
219
|
+
def edit_num_injections(self, num_inj: int, row_num: int):
|
220
|
+
self._edit_row_num(row=row_num, col_name=RegisterFlag.NUM_INJ, val=num_inj)
|
221
|
+
|
222
|
+
def edit_method_name(self, method: str, row_num: int):
|
223
|
+
method_dir = self.method_controller.src
|
224
|
+
possible_path = os.path.join(method_dir, method) + ".M\\"
|
225
|
+
if os.path.exists(possible_path):
|
226
|
+
method = os.path.join(method_dir, method)
|
227
|
+
self._edit_row_text(row=row_num, col_name=RegisterFlag.METHOD, val=method)
|
228
|
+
|
229
|
+
def edit_vial_location(self, loc: Tray, row_num: int):
|
230
|
+
loc_num = -1
|
231
|
+
if isinstance(loc, TenVialColumn):
|
232
|
+
loc_num = loc.value
|
233
|
+
elif isinstance(loc, FiftyFourVialPlate):
|
234
|
+
loc_num = loc.value()
|
235
|
+
self._edit_row_num(
|
236
|
+
row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc_num
|
237
|
+
)
|
238
|
+
|
207
239
|
def run(self, stall_while_running: bool = True):
|
208
240
|
"""
|
209
241
|
Starts the currently loaded sequence, storing data
|
210
242
|
under the <data_dir>/<sequence table name> folder.
|
211
243
|
Device must be ready.
|
212
244
|
"""
|
213
|
-
self.controller
|
214
|
-
|
245
|
+
if self.controller:
|
246
|
+
self.controller.send(Command.SAVE_METHOD_CMD)
|
247
|
+
self.controller.send(Command.SAVE_SEQUENCE_CMD)
|
248
|
+
else:
|
249
|
+
raise ValueError("Controller is offline!")
|
215
250
|
|
216
251
|
if not self.table_state:
|
217
252
|
self.table_state = self.load()
|
218
253
|
|
219
|
-
total_runtime = 0
|
254
|
+
total_runtime = 0.0
|
220
255
|
for entry in self.table_state.rows:
|
221
256
|
curr_method_runtime = self.method_controller.get_total_runtime()
|
222
257
|
loaded_method = self.method_controller.get_method_name().removesuffix(".M")
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
258
|
+
if entry.method:
|
259
|
+
method_path = entry.method.split(sep="\\")
|
260
|
+
method_name = method_path[-1]
|
261
|
+
if loaded_method != method_name:
|
262
|
+
method_dir = (
|
263
|
+
"\\".join(method_path[:-1]) + "\\"
|
264
|
+
if len(method_path) > 1
|
265
|
+
else None
|
266
|
+
)
|
267
|
+
self.method_controller.switch(
|
268
|
+
method_name=method_name, alt_method_dir=method_dir
|
269
|
+
)
|
270
|
+
curr_method_runtime = self.method_controller.get_total_runtime()
|
233
271
|
total_runtime += curr_method_runtime
|
234
272
|
|
235
273
|
timestamp = time.strftime(SEQUENCE_TIME_FORMAT)
|
236
274
|
self.send(Command.RUN_SEQUENCE_CMD.value)
|
237
275
|
self.timeout = total_runtime * 60
|
238
276
|
|
239
|
-
|
277
|
+
tries = 10
|
278
|
+
hplc_running = False
|
279
|
+
for _ in range(tries):
|
280
|
+
hplc_running = self.check_hplc_is_running()
|
281
|
+
if hplc_running:
|
282
|
+
break
|
283
|
+
|
284
|
+
if hplc_running:
|
240
285
|
folder_name = f"{self.table_state.name} {timestamp}"
|
241
286
|
data_file = SequenceDataFiles(
|
242
287
|
dir=folder_name, sequence_name=self.table_state.name
|
@@ -250,95 +295,104 @@ class SequenceController(TableController):
|
|
250
295
|
else:
|
251
296
|
warnings.warn("Run may have not completed.")
|
252
297
|
else:
|
253
|
-
raise RuntimeError("Sequence run
|
298
|
+
raise RuntimeError("Sequence run may not have started.")
|
254
299
|
|
255
300
|
@override
|
256
301
|
def fuzzy_match_most_recent_folder(
|
257
|
-
self, most_recent_folder:
|
302
|
+
self, most_recent_folder: T
|
258
303
|
) -> Result[SequenceDataFiles, str]:
|
259
|
-
if
|
260
|
-
|
261
|
-
|
262
|
-
list(filter(lambda d: most_recent_folder.dir in d, subdirs))
|
263
|
-
)
|
264
|
-
most_recent_folder.child_dirs = [
|
265
|
-
f
|
266
|
-
for f in potential_folders
|
267
|
-
if most_recent_folder.dir in f and ".M" not in f and ".D" in f
|
268
|
-
]
|
269
|
-
return Ok(most_recent_folder)
|
270
|
-
|
271
|
-
try:
|
272
|
-
potential_folders = []
|
273
|
-
for d in self.data_dirs:
|
274
|
-
subdirs = [x[0] for x in os.walk(d)]
|
304
|
+
if isinstance(most_recent_folder, SequenceDataFiles):
|
305
|
+
if os.path.isdir(most_recent_folder.dir):
|
306
|
+
subdirs = [x[0] for x in os.walk(most_recent_folder.dir)]
|
275
307
|
potential_folders = sorted(
|
276
308
|
list(filter(lambda d: most_recent_folder.dir in d, subdirs))
|
277
309
|
)
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
split_folder = path.split(os.sep)
|
285
|
-
if most_recent_folder.dir in split_folder[-1]:
|
286
|
-
parent_dirs.append(folder)
|
287
|
-
parent_dir = sorted(parent_dirs, reverse=True)[0]
|
288
|
-
|
289
|
-
potential_folders = []
|
290
|
-
for d in self.data_dirs:
|
291
|
-
subdirs = [x[0] for x in os.walk(d)]
|
292
|
-
potential_folders = sorted(
|
293
|
-
list(filter(lambda d: parent_dir in d, subdirs))
|
294
|
-
)
|
295
|
-
if len(potential_folders) > 0:
|
296
|
-
break
|
297
|
-
assert len(potential_folders) > 0
|
298
|
-
most_recent_folder.child_dirs = [
|
299
|
-
f
|
300
|
-
for f in potential_folders
|
301
|
-
if parent_dir in f and ".M" not in f and ".D" in f
|
302
|
-
]
|
303
|
-
return Ok(most_recent_folder)
|
304
|
-
except Exception as e:
|
305
|
-
error = f"Failed to get sequence folder: {e}"
|
306
|
-
return Err(error)
|
310
|
+
most_recent_folder.child_dirs = [
|
311
|
+
f
|
312
|
+
for f in potential_folders
|
313
|
+
if most_recent_folder.dir in f and ".M" not in f and ".D" in f
|
314
|
+
]
|
315
|
+
return Ok(most_recent_folder)
|
307
316
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
317
|
+
try:
|
318
|
+
potential_folders = []
|
319
|
+
for d in self.data_dirs:
|
320
|
+
subdirs = [x[0] for x in os.walk(d)]
|
321
|
+
potential_folders = sorted(
|
322
|
+
list(filter(lambda d: most_recent_folder.dir in d, subdirs))
|
323
|
+
)
|
324
|
+
if len(potential_folders) > 0:
|
325
|
+
break
|
326
|
+
assert len(potential_folders) > 0
|
327
|
+
parent_dirs = []
|
328
|
+
for folder in potential_folders:
|
329
|
+
path = os.path.normpath(folder)
|
330
|
+
split_folder = path.split(os.sep)
|
331
|
+
if most_recent_folder.dir in split_folder[-1]:
|
332
|
+
parent_dirs.append(folder)
|
333
|
+
parent_dir = sorted(parent_dirs, reverse=True)[0]
|
334
|
+
|
335
|
+
potential_folders = []
|
336
|
+
for d in self.data_dirs:
|
337
|
+
subdirs = [x[0] for x in os.walk(d)]
|
338
|
+
potential_folders = sorted(
|
339
|
+
list(filter(lambda d: parent_dir in d, subdirs))
|
340
|
+
)
|
341
|
+
if len(potential_folders) > 0:
|
342
|
+
break
|
343
|
+
assert len(potential_folders) > 0
|
344
|
+
most_recent_folder.child_dirs = [
|
345
|
+
f
|
346
|
+
for f in potential_folders
|
347
|
+
if parent_dir in f and ".M" not in f and ".D" in f
|
348
|
+
]
|
349
|
+
return Ok(most_recent_folder)
|
350
|
+
except Exception as e:
|
351
|
+
error = f"Failed to get sequence folder: {e}"
|
352
|
+
return Err(error)
|
353
|
+
return Err("Expected SequenceDataFile type.")
|
354
|
+
|
355
|
+
def get_data_mult_uv(self, custom_path: Optional[str] = None):
|
356
|
+
seq_data_dir = (
|
312
357
|
SequenceDataFiles(dir=custom_path, child_dirs=[], sequence_name="")
|
313
358
|
if custom_path
|
314
359
|
else self.data_files[-1]
|
315
360
|
)
|
316
|
-
if len(
|
361
|
+
if len(seq_data_dir.child_dirs) == 0:
|
317
362
|
self.data_files[-1] = self.fuzzy_match_most_recent_folder(
|
318
|
-
|
363
|
+
seq_data_dir
|
319
364
|
).ok_value
|
320
365
|
all_w_spectra: List[Dict[int, AgilentHPLCChromatogram]] = []
|
321
366
|
for row in self.data_files[-1].child_dirs:
|
322
|
-
self.
|
323
|
-
all_w_spectra.append(self.uv)
|
367
|
+
all_w_spectra.append(self.get_data_uv(custom_path=row))
|
324
368
|
return all_w_spectra
|
325
369
|
|
370
|
+
def get_data_uv(
|
371
|
+
self, custom_path: Optional[str] = None
|
372
|
+
) -> Dict[int, AgilentHPLCChromatogram]:
|
373
|
+
if isinstance(custom_path, str):
|
374
|
+
self.get_uv_spectrum(custom_path)
|
375
|
+
return self.uv
|
376
|
+
raise ValueError(
|
377
|
+
"Path should exist when calling from sequence. Provide a child path (contains the method)."
|
378
|
+
)
|
379
|
+
|
326
380
|
def get_data(
|
327
381
|
self, custom_path: Optional[str] = None
|
328
382
|
) -> List[AgilentChannelChromatogramData]:
|
329
|
-
|
383
|
+
seq_file_dir = (
|
330
384
|
SequenceDataFiles(dir=custom_path, child_dirs=[], sequence_name="")
|
331
385
|
if custom_path
|
332
386
|
else self.data_files[-1]
|
333
387
|
)
|
334
|
-
if len(
|
388
|
+
if len(seq_file_dir.child_dirs) == 0:
|
335
389
|
self.data_files[-1] = self.fuzzy_match_most_recent_folder(
|
336
|
-
|
390
|
+
seq_file_dir
|
337
391
|
).ok_value
|
338
392
|
spectra: List[AgilentChannelChromatogramData] = []
|
339
393
|
for row in self.data_files[-1].child_dirs:
|
340
394
|
self.get_spectrum_at_channels(row)
|
341
|
-
spectra.append(AgilentChannelChromatogramData(
|
395
|
+
spectra.append(AgilentChannelChromatogramData.from_dict(self.spectra))
|
342
396
|
return spectra
|
343
397
|
|
344
398
|
def get_report(
|
@@ -363,8 +417,12 @@ class SequenceController(TableController):
|
|
363
417
|
spectra[i].__dict__.values()
|
364
418
|
)
|
365
419
|
for j, signal in enumerate(metd_report.signals):
|
366
|
-
|
367
|
-
|
368
|
-
|
420
|
+
assert len(metd_report.signals) <= len(child_spectra)
|
421
|
+
try:
|
422
|
+
possible_data = child_spectra[j]
|
423
|
+
if len(possible_data.x) > 0:
|
424
|
+
signal.data = possible_data
|
425
|
+
except IndexError:
|
426
|
+
raise ValueError(j)
|
369
427
|
reports.append(metd_report)
|
370
428
|
return reports
|
@@ -1,18 +1,21 @@
|
|
1
|
+
from ..abc_tables.device import DeviceController
|
1
2
|
from ....control.controllers import CommunicationController
|
2
3
|
from ....utils.injector_types import (
|
3
4
|
Draw,
|
4
5
|
Inject,
|
5
|
-
InjectorFunction,
|
6
6
|
InjectorTable,
|
7
7
|
Mode,
|
8
8
|
Remote,
|
9
9
|
RemoteCommand,
|
10
10
|
SourceType,
|
11
11
|
Wait,
|
12
|
+
DrawDefault,
|
13
|
+
DrawDefaultVolume,
|
14
|
+
DrawDefaultLocation,
|
12
15
|
)
|
16
|
+
from ....utils.macro import Response
|
13
17
|
from ....utils.table_types import RegisterFlag, Table
|
14
|
-
from ....utils.tray_types import Tray
|
15
|
-
from .device import DeviceController
|
18
|
+
from ....utils.tray_types import Tray, FiftyFourVialPlate, TenVialColumn, LocationPlus
|
16
19
|
|
17
20
|
|
18
21
|
class InjectorController(DeviceController):
|
@@ -21,9 +24,32 @@ class InjectorController(DeviceController):
|
|
21
24
|
):
|
22
25
|
super().__init__(controller, table, offline)
|
23
26
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
+
def try_vial_location(self, val: str) -> Tray:
|
28
|
+
try:
|
29
|
+
return FiftyFourVialPlate.from_str(val)
|
30
|
+
except Exception:
|
31
|
+
try:
|
32
|
+
return TenVialColumn(int(val))
|
33
|
+
except Exception:
|
34
|
+
raise ValueError("Location could not be identified.")
|
35
|
+
|
36
|
+
def get_row(
|
37
|
+
self, row: int
|
38
|
+
) -> (
|
39
|
+
Draw
|
40
|
+
| DrawDefaultVolume
|
41
|
+
| Inject
|
42
|
+
| Wait
|
43
|
+
| DrawDefault
|
44
|
+
| DrawDefaultLocation
|
45
|
+
| Remote
|
46
|
+
):
|
47
|
+
def return_location_plus() -> Tray:
|
48
|
+
unit = self.get_num(row, RegisterFlag.DRAW_LOCATION_UNIT)
|
49
|
+
tray = self.get_num(row, RegisterFlag.DRAW_LOCATION_TRAY)
|
50
|
+
row_ = self.get_num(row, RegisterFlag.DRAW_LOCATION_ROW)
|
51
|
+
col = self.get_num(row, RegisterFlag.DRAW_LOCATION_COLUMN)
|
52
|
+
return LocationPlus(int(unit), int(tray), int(row_), int(col))
|
27
53
|
|
28
54
|
function = self.get_text(row, RegisterFlag.FUNCTION)
|
29
55
|
if function == "Wait":
|
@@ -31,31 +57,50 @@ class InjectorController(DeviceController):
|
|
31
57
|
elif function == "Inject":
|
32
58
|
return Inject()
|
33
59
|
elif function == "Draw":
|
34
|
-
# TODO: better error handling
|
35
60
|
is_source = SourceType(self.get_text(row, RegisterFlag.DRAW_SOURCE))
|
36
61
|
is_volume = Mode(self.get_text(row, RegisterFlag.DRAW_VOLUME))
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
62
|
+
if is_volume is not Mode.SET:
|
63
|
+
if is_source == SourceType.DEFAULT:
|
64
|
+
return DrawDefault()
|
65
|
+
elif is_source is SourceType.SPECIFIC_LOCATION:
|
66
|
+
return DrawDefaultVolume(location=return_location_plus())
|
67
|
+
elif is_source is SourceType.LOCATION:
|
68
|
+
return DrawDefaultVolume(
|
69
|
+
location=self.try_vial_location(
|
70
|
+
self.get_text(row, RegisterFlag.DRAW_LOCATION)
|
71
|
+
)
|
72
|
+
)
|
73
|
+
else:
|
74
|
+
vol = self.get_num(row, RegisterFlag.DRAW_VOLUME_VALUE)
|
75
|
+
if is_source == SourceType.DEFAULT:
|
76
|
+
return DrawDefaultLocation(amount=vol)
|
77
|
+
elif is_source is SourceType.SPECIFIC_LOCATION:
|
78
|
+
return Draw(amount=vol, location=return_location_plus())
|
79
|
+
elif is_source is SourceType.LOCATION:
|
80
|
+
return Draw(
|
81
|
+
amount=vol,
|
82
|
+
location=self.try_vial_location(
|
83
|
+
self.get_text(row, RegisterFlag.DRAW_LOCATION)
|
84
|
+
),
|
85
|
+
)
|
48
86
|
elif function == "Remote":
|
49
87
|
return Remote(
|
50
88
|
command=RemoteCommand(self.get_text(row, RegisterFlag.REMOTE)),
|
51
|
-
duration=self.get_num(row, RegisterFlag.REMOTE_DUR),
|
89
|
+
duration=int(self.get_num(row, RegisterFlag.REMOTE_DUR)),
|
52
90
|
)
|
91
|
+
raise ValueError("No valid function found.")
|
53
92
|
|
54
93
|
def load(self) -> InjectorTable:
|
55
94
|
rows = self.get_num_rows()
|
56
95
|
if rows.is_ok():
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
96
|
+
row_response = rows.value
|
97
|
+
if isinstance(row_response, Response):
|
98
|
+
return InjectorTable(
|
99
|
+
functions=[
|
100
|
+
self.get_row(i + 1)
|
101
|
+
for i in range(int(row_response.num_response))
|
102
|
+
]
|
103
|
+
)
|
104
|
+
elif rows.is_err():
|
105
|
+
return InjectorTable(functions=[])
|
106
|
+
raise ValueError("Unexpected error")
|