pychemstation 0.10.6__py3-none-any.whl → 0.10.7.dev1__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/base_spectrum.py +14 -15
- pychemstation/analysis/chromatogram.py +7 -8
- pychemstation/analysis/process_report.py +7 -15
- pychemstation/control/README.md +2 -2
- pychemstation/control/controllers/__init__.py +2 -1
- pychemstation/control/controllers/comm.py +40 -13
- pychemstation/control/controllers/data_aq/method.py +19 -22
- pychemstation/control/controllers/data_aq/sequence.py +129 -111
- pychemstation/control/controllers/devices/injector.py +7 -7
- pychemstation/control/hplc.py +57 -60
- pychemstation/utils/__init__.py +23 -0
- pychemstation/utils/{mocking → abc_tables}/abc_comm.py +8 -14
- pychemstation/utils/abc_tables/device.py +27 -0
- pychemstation/{control/controllers → utils}/abc_tables/run.py +69 -34
- pychemstation/{control/controllers → utils}/abc_tables/table.py +29 -22
- pychemstation/utils/macro.py +13 -0
- pychemstation/utils/method_types.py +12 -13
- pychemstation/utils/mocking/mock_comm.py +1 -1
- pychemstation/utils/num_utils.py +3 -3
- pychemstation/utils/sequence_types.py +30 -12
- pychemstation/utils/spec_utils.py +42 -66
- pychemstation/utils/table_types.py +13 -2
- pychemstation/utils/tray_types.py +28 -16
- {pychemstation-0.10.6.dist-info → pychemstation-0.10.7.dev1.dist-info}/METADATA +2 -8
- pychemstation-0.10.7.dev1.dist-info/RECORD +41 -0
- pychemstation/control/controllers/abc_tables/device.py +0 -15
- pychemstation/utils/pump_types.py +0 -7
- pychemstation-0.10.6.dist-info/RECORD +0 -42
- /pychemstation/{control/controllers → utils}/abc_tables/__init__.py +0 -0
- {pychemstation-0.10.6.dist-info → pychemstation-0.10.7.dev1.dist-info}/WHEEL +0 -0
- {pychemstation-0.10.6.dist-info → pychemstation-0.10.7.dev1.dist-info}/licenses/LICENSE +0 -0
@@ -1,19 +1,19 @@
|
|
1
1
|
import os
|
2
2
|
import time
|
3
3
|
import warnings
|
4
|
-
from typing import Any, Dict, List, Optional, Union
|
4
|
+
from typing import Any, Dict, List, Optional, Union, Set
|
5
5
|
|
6
6
|
from result import Err, Ok, Result
|
7
7
|
from typing_extensions import override
|
8
8
|
|
9
9
|
from pychemstation.analysis.chromatogram import (
|
10
|
-
SEQUENCE_TIME_FORMAT,
|
11
10
|
AgilentChannelChromatogramData,
|
12
11
|
AgilentHPLCChromatogram,
|
13
12
|
)
|
14
13
|
|
15
14
|
from ....analysis.process_report import AgilentReport, ReportType
|
16
15
|
from ....control.controllers.comm import CommunicationController
|
16
|
+
from ....utils.abc_tables.run import RunController
|
17
17
|
from ....utils.macro import Command
|
18
18
|
from ....utils.sequence_types import (
|
19
19
|
InjectionSource,
|
@@ -23,8 +23,7 @@ from ....utils.sequence_types import (
|
|
23
23
|
SequenceTable,
|
24
24
|
)
|
25
25
|
from ....utils.table_types import RegisterFlag, T, Table
|
26
|
-
from ....utils.tray_types import FiftyFourVialPlate,
|
27
|
-
from ..abc_tables.run import RunController
|
26
|
+
from ....utils.tray_types import FiftyFourVialPlate, VialBar, Tray
|
28
27
|
from . import MethodController
|
29
28
|
|
30
29
|
|
@@ -37,8 +36,8 @@ class SequenceController(RunController):
|
|
37
36
|
self,
|
38
37
|
controller: Optional[CommunicationController],
|
39
38
|
method_controller: MethodController,
|
40
|
-
src: str,
|
41
|
-
data_dirs: List[str],
|
39
|
+
src: Optional[str],
|
40
|
+
data_dirs: Optional[List[str]],
|
42
41
|
table: Table,
|
43
42
|
offline: bool,
|
44
43
|
):
|
@@ -81,17 +80,14 @@ class SequenceController(RunController):
|
|
81
80
|
|
82
81
|
def try_vial_location(self, val: Any) -> Tray:
|
83
82
|
try:
|
84
|
-
return (
|
85
|
-
TenVialColumn(val)
|
86
|
-
if val <= 10
|
87
|
-
else FiftyFourVialPlate.from_int(num=val)
|
88
|
-
)
|
83
|
+
return VialBar(val) if val <= 10 else FiftyFourVialPlate.from_int(num=val)
|
89
84
|
except ValueError:
|
90
85
|
raise ValueError("Expected vial location, is empty.")
|
91
86
|
|
92
87
|
def get_row(self, row: int) -> SequenceEntry:
|
93
88
|
sample_name = self.get_text(row, RegisterFlag.NAME)
|
94
89
|
vial_location = self.try_int(self.get_num(row, RegisterFlag.VIAL_LOCATION))
|
90
|
+
data_file = self.get_text(row, RegisterFlag.DATA_FILE)
|
95
91
|
method = self.get_text(row, RegisterFlag.METHOD)
|
96
92
|
num_inj = self.try_int(self.get_num(row, RegisterFlag.NUM_INJ))
|
97
93
|
inj_vol = self.try_float(self.get_text(row, RegisterFlag.INJ_VOL))
|
@@ -106,17 +102,9 @@ class SequenceController(RunController):
|
|
106
102
|
inj_vol=inj_vol,
|
107
103
|
inj_source=inj_source,
|
108
104
|
sample_type=sample_type,
|
105
|
+
data_file=data_file,
|
109
106
|
)
|
110
107
|
|
111
|
-
def check(self) -> str:
|
112
|
-
time.sleep(2)
|
113
|
-
self.send(Command.GET_SEQUENCE_CMD)
|
114
|
-
time.sleep(2)
|
115
|
-
res = self.receive()
|
116
|
-
if res.is_ok():
|
117
|
-
return res.ok_value.string_response
|
118
|
-
return "ERROR"
|
119
|
-
|
120
108
|
def switch(self, seq_name: str):
|
121
109
|
"""
|
122
110
|
Switch to the specified sequence. The sequence name does not need the '.S' extension.
|
@@ -127,13 +115,17 @@ class SequenceController(RunController):
|
|
127
115
|
self.send(f'_SeqPath$ = "{self.src}"')
|
128
116
|
self.send(Command.SWITCH_SEQUENCE_CMD)
|
129
117
|
time.sleep(2)
|
130
|
-
self.
|
131
|
-
time.sleep(2)
|
132
|
-
parsed_response = self.receive().ok_value.string_response
|
118
|
+
parsed_response = self.get_current_sequence_name()
|
133
119
|
|
134
120
|
assert parsed_response == f"{seq_name}.S", "Switching sequence failed."
|
135
121
|
self.table_state = self.load()
|
136
122
|
|
123
|
+
def get_current_sequence_name(self):
|
124
|
+
self.send(Command.GET_SEQUENCE_CMD)
|
125
|
+
time.sleep(2)
|
126
|
+
parsed_response = self.receive().ok_value.string_response
|
127
|
+
return parsed_response
|
128
|
+
|
137
129
|
def edit(self, sequence_table: SequenceTable):
|
138
130
|
"""
|
139
131
|
Updates the currently loaded sequence table with the provided table. This method will delete the existing sequence table and remake it.
|
@@ -151,13 +143,13 @@ class SequenceController(RunController):
|
|
151
143
|
self.send(Command.SAVE_SEQUENCE_CMD)
|
152
144
|
for i in range(int(wanted_row_num)):
|
153
145
|
self.add_row()
|
154
|
-
self.
|
146
|
+
self.save()
|
155
147
|
self.send(Command.SWITCH_SEQUENCE_CMD)
|
156
148
|
|
157
149
|
for i, row in enumerate(sequence_table.rows):
|
158
150
|
self._edit_row(row=row, row_num=i + 1)
|
159
151
|
self.sleep(1)
|
160
|
-
self.
|
152
|
+
self.save()
|
161
153
|
self.send(Command.SWITCH_SEQUENCE_CMD)
|
162
154
|
|
163
155
|
def _edit_row(self, row: SequenceEntry, row_num: int):
|
@@ -171,70 +163,91 @@ class SequenceController(RunController):
|
|
171
163
|
if num_rows.is_ok():
|
172
164
|
while num_rows.ok_value.num_response < row_num:
|
173
165
|
self.add_row()
|
174
|
-
self.
|
166
|
+
self.save()
|
175
167
|
num_rows = self.get_num_rows()
|
176
168
|
if row.vial_location:
|
177
|
-
self.edit_vial_location(row.vial_location, row_num)
|
169
|
+
self.edit_vial_location(row.vial_location, row_num, save=False)
|
178
170
|
if row.method:
|
179
|
-
self.edit_method_name(row.method, row_num)
|
171
|
+
self.edit_method_name(row.method, row_num, save=False)
|
180
172
|
if row.num_inj:
|
181
|
-
self.edit_num_injections(row.num_inj, row_num)
|
173
|
+
self.edit_num_injections(row.num_inj, row_num, save=False)
|
182
174
|
if row.inj_vol:
|
183
|
-
self.edit_injection_volume(row.inj_vol, row_num)
|
175
|
+
self.edit_injection_volume(row.inj_vol, row_num, save=False)
|
184
176
|
if row.inj_source:
|
185
|
-
self.edit_injection_source(row.inj_source, row_num)
|
177
|
+
self.edit_injection_source(row.inj_source, row_num, save=False)
|
186
178
|
if row.sample_name:
|
187
|
-
self.edit_sample_name(row.sample_name, row_num)
|
179
|
+
self.edit_sample_name(row.sample_name, row_num, save=False)
|
188
180
|
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)
|
181
|
+
self.edit_data_file(row.data_file, row_num, save=False)
|
192
182
|
if row.sample_type:
|
193
|
-
self.edit_sample_type(row.sample_type, row_num)
|
194
|
-
self.
|
183
|
+
self.edit_sample_type(row.sample_type, row_num, save=False)
|
184
|
+
self.save()
|
195
185
|
|
196
|
-
def edit_sample_type(
|
186
|
+
def edit_sample_type(
|
187
|
+
self, sample_type: SampleType, row_num: int, save: bool = True
|
188
|
+
):
|
197
189
|
self._edit_row_num(
|
198
190
|
row=row_num,
|
199
191
|
col_name=RegisterFlag.SAMPLE_TYPE,
|
200
192
|
val=sample_type.value,
|
201
193
|
)
|
194
|
+
if save:
|
195
|
+
self.save()
|
202
196
|
|
203
|
-
def edit_data_file(self, data_file: str, row_num: int):
|
197
|
+
def edit_data_file(self, data_file: str, row_num: int, save: bool = True):
|
204
198
|
self._edit_row_text(row=row_num, col_name=RegisterFlag.DATA_FILE, val=data_file)
|
199
|
+
if save:
|
200
|
+
self.save()
|
205
201
|
|
206
|
-
def edit_sample_name(self, sample_name: str, row_num: int):
|
202
|
+
def edit_sample_name(self, sample_name: str, row_num: int, save: bool = True):
|
207
203
|
self._edit_row_text(row=row_num, col_name=RegisterFlag.NAME, val=sample_name)
|
204
|
+
if save:
|
205
|
+
self.save()
|
208
206
|
|
209
|
-
def edit_injection_source(
|
207
|
+
def edit_injection_source(
|
208
|
+
self, inj_source: InjectionSource, row_num: int, save: bool = True
|
209
|
+
):
|
210
210
|
self._edit_row_text(
|
211
211
|
row=row_num, col_name=RegisterFlag.INJ_SOR, val=inj_source.value
|
212
212
|
)
|
213
|
+
if save:
|
214
|
+
self.save()
|
213
215
|
|
214
|
-
def edit_injection_volume(
|
216
|
+
def edit_injection_volume(
|
217
|
+
self, inj_vol: Union[int, float], row_num: int, save: bool = True
|
218
|
+
):
|
215
219
|
self._edit_row_text(
|
216
220
|
row=row_num, col_name=RegisterFlag.INJ_VOL, val=str(inj_vol)
|
217
221
|
)
|
222
|
+
if save:
|
223
|
+
self.save()
|
218
224
|
|
219
|
-
def edit_num_injections(self, num_inj: int, row_num: int):
|
225
|
+
def edit_num_injections(self, num_inj: int, row_num: int, save: bool = True):
|
220
226
|
self._edit_row_num(row=row_num, col_name=RegisterFlag.NUM_INJ, val=num_inj)
|
221
227
|
|
222
|
-
def edit_method_name(self, method: str, row_num: int):
|
228
|
+
def edit_method_name(self, method: str, row_num: int, save: bool = True):
|
223
229
|
method_dir = self.method_controller.src
|
224
230
|
possible_path = os.path.join(method_dir, method) + ".M\\"
|
225
231
|
if os.path.exists(possible_path):
|
226
232
|
method = os.path.join(method_dir, method)
|
227
233
|
self._edit_row_text(row=row_num, col_name=RegisterFlag.METHOD, val=method)
|
234
|
+
if save:
|
235
|
+
self.save()
|
228
236
|
|
229
|
-
def edit_vial_location(self, loc: Tray, row_num: int):
|
237
|
+
def edit_vial_location(self, loc: Tray, row_num: int, save: bool = True):
|
230
238
|
loc_num = -1
|
231
|
-
if isinstance(loc,
|
239
|
+
if isinstance(loc, VialBar):
|
232
240
|
loc_num = loc.value
|
233
241
|
elif isinstance(loc, FiftyFourVialPlate):
|
234
242
|
loc_num = loc.value()
|
235
243
|
self._edit_row_num(
|
236
244
|
row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc_num
|
237
245
|
)
|
246
|
+
if save:
|
247
|
+
self.save()
|
248
|
+
|
249
|
+
def save(self):
|
250
|
+
self.send(Command.SAVE_SEQUENCE_CMD)
|
238
251
|
|
239
252
|
def run(self, stall_while_running: bool = True):
|
240
253
|
"""
|
@@ -248,7 +261,8 @@ class SequenceController(RunController):
|
|
248
261
|
else:
|
249
262
|
raise ValueError("Controller is offline!")
|
250
263
|
|
251
|
-
|
264
|
+
current_sequence_name = self.get_current_sequence_name()
|
265
|
+
if not self.table_state or self.table_state.name not in current_sequence_name:
|
252
266
|
self.table_state = self.load()
|
253
267
|
|
254
268
|
total_runtime = 0.0
|
@@ -270,7 +284,6 @@ class SequenceController(RunController):
|
|
270
284
|
curr_method_runtime = self.method_controller.get_total_runtime()
|
271
285
|
total_runtime += curr_method_runtime
|
272
286
|
|
273
|
-
timestamp = time.strftime(SEQUENCE_TIME_FORMAT)
|
274
287
|
self.send(Command.RUN_SEQUENCE_CMD.value)
|
275
288
|
self.timeout = total_runtime * 60
|
276
289
|
|
@@ -282,71 +295,70 @@ class SequenceController(RunController):
|
|
282
295
|
break
|
283
296
|
|
284
297
|
if hplc_running:
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
298
|
+
full_path_name, current_sample_file = None, None
|
299
|
+
for _ in range(5):
|
300
|
+
try:
|
301
|
+
full_path_name, current_sample_file = (
|
302
|
+
self.get_current_run_data_dir_file()
|
303
|
+
)
|
304
|
+
break
|
305
|
+
except ValueError:
|
306
|
+
pass
|
307
|
+
if full_path_name and current_sample_file:
|
308
|
+
data_file = SequenceDataFiles(
|
309
|
+
sequence_name=self.table_state.name,
|
310
|
+
dir=full_path_name,
|
311
|
+
_data_files=[r.data_file for r in self.table_state.rows],
|
312
|
+
child_dirs=[os.path.join(full_path_name, current_sample_file)],
|
313
|
+
)
|
314
|
+
self.data_files.append(data_file)
|
315
|
+
else:
|
316
|
+
raise ValueError("Data directory for sequence was not found.")
|
290
317
|
|
291
318
|
if stall_while_running:
|
292
319
|
run_completed = self.check_hplc_done_running()
|
293
320
|
if run_completed.is_ok():
|
294
321
|
self.data_files[-1] = run_completed.ok_value
|
295
322
|
else:
|
296
|
-
warnings.warn(
|
323
|
+
warnings.warn(run_completed.err_value)
|
297
324
|
else:
|
298
325
|
raise RuntimeError("Sequence run may not have started.")
|
299
326
|
|
300
327
|
@override
|
301
|
-
def
|
302
|
-
self, most_recent_folder: T
|
328
|
+
def _fuzzy_match_most_recent_folder(
|
329
|
+
self, most_recent_folder: T, child_dirs: Optional[Set[str]]
|
303
330
|
) -> Result[SequenceDataFiles, str]:
|
304
331
|
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)]
|
307
|
-
potential_folders = sorted(
|
308
|
-
list(filter(lambda d: most_recent_folder.dir in d, subdirs))
|
309
|
-
)
|
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)
|
316
|
-
|
317
332
|
try:
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
if parent_dir in f and ".M" not in f and ".D" in f
|
348
|
-
]
|
349
|
-
return Ok(most_recent_folder)
|
333
|
+
if most_recent_folder.dir and os.path.isdir(most_recent_folder.dir):
|
334
|
+
subdirs = [x[0] for x in os.walk(most_recent_folder.dir)]
|
335
|
+
if (
|
336
|
+
child_dirs
|
337
|
+
and len(child_dirs) > 0
|
338
|
+
and set(most_recent_folder._data_files) == child_dirs
|
339
|
+
):
|
340
|
+
most_recent_folder.child_dirs = [
|
341
|
+
os.path.join(most_recent_folder.dir, f) for f in child_dirs
|
342
|
+
]
|
343
|
+
else:
|
344
|
+
potential_folders: List[str] = sorted(
|
345
|
+
list(
|
346
|
+
filter(
|
347
|
+
lambda d: most_recent_folder.dir in d,
|
348
|
+
subdirs,
|
349
|
+
)
|
350
|
+
)
|
351
|
+
)
|
352
|
+
most_recent_folder.child_dirs = [
|
353
|
+
f
|
354
|
+
for f in potential_folders
|
355
|
+
if most_recent_folder.dir in f
|
356
|
+
and ".M" not in f
|
357
|
+
and ".D" in f
|
358
|
+
]
|
359
|
+
return Ok(most_recent_folder)
|
360
|
+
else:
|
361
|
+
return Err("No sequence folder found, please give the full path.")
|
350
362
|
except Exception as e:
|
351
363
|
error = f"Failed to get sequence folder: {e}"
|
352
364
|
return Err(error)
|
@@ -354,16 +366,20 @@ class SequenceController(RunController):
|
|
354
366
|
|
355
367
|
def get_data_mult_uv(self, custom_path: Optional[str] = None):
|
356
368
|
seq_data_dir = (
|
357
|
-
SequenceDataFiles(dir=custom_path,
|
369
|
+
SequenceDataFiles(dir=custom_path, sequence_name="")
|
358
370
|
if custom_path
|
359
371
|
else self.data_files[-1]
|
360
372
|
)
|
361
373
|
if len(seq_data_dir.child_dirs) == 0:
|
362
|
-
|
363
|
-
seq_data_dir
|
364
|
-
)
|
374
|
+
search_folder = self._fuzzy_match_most_recent_folder(
|
375
|
+
seq_data_dir, set(seq_data_dir.child_dirs)
|
376
|
+
)
|
377
|
+
if search_folder.is_ok():
|
378
|
+
seq_data_dir = search_folder.ok_value
|
379
|
+
else:
|
380
|
+
raise FileNotFoundError(search_folder.err_value)
|
365
381
|
all_w_spectra: List[Dict[int, AgilentHPLCChromatogram]] = []
|
366
|
-
for row in
|
382
|
+
for row in seq_data_dir.child_dirs:
|
367
383
|
all_w_spectra.append(self.get_data_uv(custom_path=row))
|
368
384
|
return all_w_spectra
|
369
385
|
|
@@ -381,13 +397,13 @@ class SequenceController(RunController):
|
|
381
397
|
self, custom_path: Optional[str] = None
|
382
398
|
) -> List[AgilentChannelChromatogramData]:
|
383
399
|
seq_file_dir = (
|
384
|
-
SequenceDataFiles(dir=custom_path,
|
400
|
+
SequenceDataFiles(dir=custom_path, sequence_name="")
|
385
401
|
if custom_path
|
386
402
|
else self.data_files[-1]
|
387
403
|
)
|
388
404
|
if len(seq_file_dir.child_dirs) == 0:
|
389
|
-
self.data_files[-1] = self.
|
390
|
-
seq_file_dir
|
405
|
+
self.data_files[-1] = self._fuzzy_match_most_recent_folder(
|
406
|
+
seq_file_dir, set(seq_file_dir.child_dirs)
|
391
407
|
).ok_value
|
392
408
|
spectra: List[AgilentChannelChromatogramData] = []
|
393
409
|
for row in self.data_files[-1].child_dirs:
|
@@ -402,10 +418,12 @@ class SequenceController(RunController):
|
|
402
418
|
) -> List[AgilentReport]:
|
403
419
|
if custom_path:
|
404
420
|
self.data_files.append(
|
405
|
-
self.
|
421
|
+
self._fuzzy_match_most_recent_folder(
|
406
422
|
most_recent_folder=SequenceDataFiles(
|
407
|
-
dir=custom_path,
|
408
|
-
|
423
|
+
dir=custom_path,
|
424
|
+
sequence_name="NA",
|
425
|
+
),
|
426
|
+
child_dirs=None,
|
409
427
|
).ok_value
|
410
428
|
)
|
411
429
|
parent_dir = self.data_files[-1]
|
@@ -1,5 +1,7 @@
|
|
1
|
-
from
|
1
|
+
from __future__ import annotations
|
2
|
+
|
2
3
|
from ....control.controllers import CommunicationController
|
4
|
+
from ....utils.abc_tables.device import DeviceController
|
3
5
|
from ....utils.injector_types import (
|
4
6
|
Draw,
|
5
7
|
Inject,
|
@@ -15,7 +17,7 @@ from ....utils.injector_types import (
|
|
15
17
|
)
|
16
18
|
from ....utils.macro import Response
|
17
19
|
from ....utils.table_types import RegisterFlag, Table
|
18
|
-
from ....utils.tray_types import Tray, FiftyFourVialPlate,
|
20
|
+
from ....utils.tray_types import Tray, FiftyFourVialPlate, VialBar, LocationPlus
|
19
21
|
|
20
22
|
|
21
23
|
class InjectorController(DeviceController):
|
@@ -29,7 +31,7 @@ class InjectorController(DeviceController):
|
|
29
31
|
return FiftyFourVialPlate.from_str(val)
|
30
32
|
except Exception:
|
31
33
|
try:
|
32
|
-
return
|
34
|
+
return VialBar(int(val))
|
33
35
|
except Exception:
|
34
36
|
raise ValueError("Location could not be identified.")
|
35
37
|
|
@@ -90,7 +92,7 @@ class InjectorController(DeviceController):
|
|
90
92
|
)
|
91
93
|
raise ValueError("No valid function found.")
|
92
94
|
|
93
|
-
def load(self) -> InjectorTable:
|
95
|
+
def load(self) -> InjectorTable | None:
|
94
96
|
rows = self.get_num_rows()
|
95
97
|
if rows.is_ok():
|
96
98
|
row_response = rows.value
|
@@ -101,6 +103,4 @@ class InjectorController(DeviceController):
|
|
101
103
|
for i in range(int(row_response.num_response))
|
102
104
|
]
|
103
105
|
)
|
104
|
-
|
105
|
-
return InjectorTable(functions=[])
|
106
|
-
raise ValueError("Unexpected error")
|
106
|
+
raise ValueError("Couldn't read injector table rows.")
|