pychemstation 0.10.7__py3-none-any.whl → 0.10.8__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 +10 -16
- pychemstation/control/README.md +1 -1
- pychemstation/control/controllers/__init__.py +2 -1
- pychemstation/control/controllers/comm.py +33 -27
- pychemstation/control/controllers/data_aq/method.py +233 -79
- pychemstation/control/controllers/data_aq/sequence.py +104 -84
- pychemstation/control/controllers/devices/injector.py +3 -3
- pychemstation/control/hplc.py +53 -41
- pychemstation/utils/__init__.py +23 -0
- pychemstation/{control/controllers → utils}/abc_tables/abc_comm.py +15 -13
- pychemstation/{control/controllers → utils}/abc_tables/device.py +9 -2
- pychemstation/{control/controllers → utils}/abc_tables/run.py +45 -37
- pychemstation/{control/controllers → utils}/abc_tables/table.py +32 -29
- pychemstation/utils/macro.py +7 -2
- pychemstation/utils/method_types.py +13 -14
- pychemstation/utils/mocking/mock_comm.py +25 -2
- pychemstation/utils/mocking/mock_hplc.py +29 -1
- pychemstation/utils/num_utils.py +3 -3
- pychemstation/utils/sequence_types.py +30 -14
- pychemstation/utils/spec_utils.py +42 -66
- pychemstation/utils/table_types.py +15 -2
- pychemstation/utils/tray_types.py +28 -16
- {pychemstation-0.10.7.dist-info → pychemstation-0.10.8.dist-info}/METADATA +1 -7
- pychemstation-0.10.8.dist-info/RECORD +41 -0
- pychemstation/utils/pump_types.py +0 -7
- pychemstation-0.10.7.dist-info/RECORD +0 -42
- /pychemstation/{control/controllers → utils}/abc_tables/__init__.py +0 -0
- {pychemstation-0.10.7.dist-info → pychemstation-0.10.8.dist-info}/WHEEL +0 -0
- {pychemstation-0.10.7.dist-info → pychemstation-0.10.8.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,9 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import os
|
2
4
|
import time
|
3
5
|
import warnings
|
4
|
-
from typing import Any, Dict, List, Optional, Union,
|
6
|
+
from typing import Any, Dict, List, Optional, Union, Tuple
|
5
7
|
|
6
8
|
from result import Err, Ok, Result
|
7
9
|
from typing_extensions import override
|
@@ -9,10 +11,12 @@ from typing_extensions import override
|
|
9
11
|
from pychemstation.analysis.chromatogram import (
|
10
12
|
AgilentChannelChromatogramData,
|
11
13
|
AgilentHPLCChromatogram,
|
14
|
+
SEQUENCE_TIME_FORMAT,
|
12
15
|
)
|
13
16
|
|
14
17
|
from ....analysis.process_report import AgilentReport, ReportType
|
15
18
|
from ....control.controllers.comm import CommunicationController
|
19
|
+
from ....utils.abc_tables.run import RunController
|
16
20
|
from ....utils.macro import Command
|
17
21
|
from ....utils.sequence_types import (
|
18
22
|
InjectionSource,
|
@@ -22,8 +26,7 @@ from ....utils.sequence_types import (
|
|
22
26
|
SequenceTable,
|
23
27
|
)
|
24
28
|
from ....utils.table_types import RegisterFlag, T, Table
|
25
|
-
from ....utils.tray_types import FiftyFourVialPlate,
|
26
|
-
from ..abc_tables.run import RunController
|
29
|
+
from ....utils.tray_types import FiftyFourVialPlate, VialBar, Tray
|
27
30
|
from . import MethodController
|
28
31
|
|
29
32
|
|
@@ -36,8 +39,8 @@ class SequenceController(RunController):
|
|
36
39
|
self,
|
37
40
|
controller: Optional[CommunicationController],
|
38
41
|
method_controller: MethodController,
|
39
|
-
src: str,
|
40
|
-
data_dirs: List[str],
|
42
|
+
src: Optional[str],
|
43
|
+
data_dirs: Optional[List[str]],
|
41
44
|
table: Table,
|
42
45
|
offline: bool,
|
43
46
|
):
|
@@ -80,11 +83,7 @@ class SequenceController(RunController):
|
|
80
83
|
|
81
84
|
def try_vial_location(self, val: Any) -> Tray:
|
82
85
|
try:
|
83
|
-
return (
|
84
|
-
TenVialColumn(val)
|
85
|
-
if val <= 10
|
86
|
-
else FiftyFourVialPlate.from_int(num=val)
|
87
|
-
)
|
86
|
+
return VialBar(val) if val <= 10 else FiftyFourVialPlate.from_int(num=val)
|
88
87
|
except ValueError:
|
89
88
|
raise ValueError("Expected vial location, is empty.")
|
90
89
|
|
@@ -109,15 +108,6 @@ class SequenceController(RunController):
|
|
109
108
|
data_file=data_file,
|
110
109
|
)
|
111
110
|
|
112
|
-
def check(self) -> str:
|
113
|
-
time.sleep(2)
|
114
|
-
self.send(Command.GET_SEQUENCE_CMD)
|
115
|
-
time.sleep(2)
|
116
|
-
res = self.receive()
|
117
|
-
if res.is_ok():
|
118
|
-
return res.ok_value.string_response
|
119
|
-
return "ERROR"
|
120
|
-
|
121
111
|
def switch(self, seq_name: str):
|
122
112
|
"""
|
123
113
|
Switch to the specified sequence. The sequence name does not need the '.S' extension.
|
@@ -131,7 +121,6 @@ class SequenceController(RunController):
|
|
131
121
|
parsed_response = self.get_current_sequence_name()
|
132
122
|
|
133
123
|
assert parsed_response == f"{seq_name}.S", "Switching sequence failed."
|
134
|
-
self.table_state = self.load()
|
135
124
|
|
136
125
|
def get_current_sequence_name(self):
|
137
126
|
self.send(Command.GET_SEQUENCE_CMD)
|
@@ -199,6 +188,8 @@ class SequenceController(RunController):
|
|
199
188
|
def edit_sample_type(
|
200
189
|
self, sample_type: SampleType, row_num: int, save: bool = True
|
201
190
|
):
|
191
|
+
if not isinstance(sample_type, SampleType):
|
192
|
+
raise ValueError("`sample_type` should be of type `SampleType`")
|
202
193
|
self._edit_row_num(
|
203
194
|
row=row_num,
|
204
195
|
col_name=RegisterFlag.SAMPLE_TYPE,
|
@@ -220,6 +211,8 @@ class SequenceController(RunController):
|
|
220
211
|
def edit_injection_source(
|
221
212
|
self, inj_source: InjectionSource, row_num: int, save: bool = True
|
222
213
|
):
|
214
|
+
if not isinstance(inj_source, InjectionSource):
|
215
|
+
raise ValueError("`inj_source` should be of type `InjectionSource`")
|
223
216
|
self._edit_row_text(
|
224
217
|
row=row_num, col_name=RegisterFlag.INJ_SOR, val=inj_source.value
|
225
218
|
)
|
@@ -237,25 +230,65 @@ class SequenceController(RunController):
|
|
237
230
|
|
238
231
|
def edit_num_injections(self, num_inj: int, row_num: int, save: bool = True):
|
239
232
|
self._edit_row_num(row=row_num, col_name=RegisterFlag.NUM_INJ, val=num_inj)
|
233
|
+
if save:
|
234
|
+
self.save()
|
240
235
|
|
241
|
-
def edit_method_name(
|
236
|
+
def edit_method_name(
|
237
|
+
self, method: str, row_num: int, save: bool = True, override_check: bool = False
|
238
|
+
):
|
242
239
|
method_dir = self.method_controller.src
|
243
240
|
possible_path = os.path.join(method_dir, method) + ".M\\"
|
244
241
|
if os.path.exists(possible_path):
|
245
242
|
method = os.path.join(method_dir, method)
|
243
|
+
elif not override_check:
|
244
|
+
raise ValueError(
|
245
|
+
"Method may not exist. If you would still like to use this method, set the `override_check` flag to `True`"
|
246
|
+
)
|
246
247
|
self._edit_row_text(row=row_num, col_name=RegisterFlag.METHOD, val=method)
|
247
248
|
if save:
|
248
249
|
self.save()
|
249
250
|
|
250
251
|
def edit_vial_location(self, loc: Tray, row_num: int, save: bool = True):
|
251
252
|
loc_num = -1
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
253
|
+
try:
|
254
|
+
previous_contents = self.get_row(row_num)
|
255
|
+
if (
|
256
|
+
isinstance(loc, VialBar)
|
257
|
+
and isinstance(previous_contents.vial_location, VialBar)
|
258
|
+
or isinstance(loc, FiftyFourVialPlate)
|
259
|
+
and isinstance(previous_contents.vial_location, FiftyFourVialPlate)
|
260
|
+
):
|
261
|
+
if isinstance(loc, VialBar):
|
262
|
+
loc_num = loc.value
|
263
|
+
elif isinstance(loc, FiftyFourVialPlate):
|
264
|
+
loc_num = loc.value()
|
265
|
+
self._edit_row_num(
|
266
|
+
row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc_num
|
267
|
+
)
|
268
|
+
elif isinstance(loc, VialBar) or isinstance(loc, FiftyFourVialPlate):
|
269
|
+
self.add_row()
|
270
|
+
previous_contents.vial_location = loc
|
271
|
+
num_rows = self.get_num_rows().ok_value.num_response
|
272
|
+
self._edit_row(previous_contents, num_rows)
|
273
|
+
self.move_row(int(num_rows), row_num)
|
274
|
+
self.delete_row(row_num + 1)
|
275
|
+
self.save()
|
276
|
+
else:
|
277
|
+
raise ValueError(
|
278
|
+
"`loc` should be of type `VialBar`, `FiftyFourVialPlate`"
|
279
|
+
)
|
280
|
+
except Exception:
|
281
|
+
if not (isinstance(loc, VialBar) or isinstance(loc, FiftyFourVialPlate)):
|
282
|
+
raise ValueError(
|
283
|
+
"`loc` should be of type `VialBar`, `FiftyFourVialPlate`"
|
284
|
+
)
|
285
|
+
if isinstance(loc, VialBar):
|
286
|
+
loc_num = loc.value
|
287
|
+
elif isinstance(loc, FiftyFourVialPlate):
|
288
|
+
loc_num = loc.value()
|
289
|
+
self._edit_row_num(
|
290
|
+
row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc_num
|
291
|
+
)
|
259
292
|
if save:
|
260
293
|
self.save()
|
261
294
|
|
@@ -268,11 +301,6 @@ class SequenceController(RunController):
|
|
268
301
|
under the <data_dir>/<sequence table name> folder.
|
269
302
|
Device must be ready.
|
270
303
|
"""
|
271
|
-
if self.controller:
|
272
|
-
self.controller.send(Command.SAVE_METHOD_CMD)
|
273
|
-
self.controller.send(Command.SAVE_SEQUENCE_CMD)
|
274
|
-
else:
|
275
|
-
raise ValueError("Controller is offline!")
|
276
304
|
|
277
305
|
current_sequence_name = self.get_current_sequence_name()
|
278
306
|
if not self.table_state or self.table_state.name not in current_sequence_name:
|
@@ -297,6 +325,11 @@ class SequenceController(RunController):
|
|
297
325
|
curr_method_runtime = self.method_controller.get_total_runtime()
|
298
326
|
total_runtime += curr_method_runtime
|
299
327
|
|
328
|
+
timestamp = time.strftime(SEQUENCE_TIME_FORMAT)
|
329
|
+
folder_name = f"{self.table_state.name} {timestamp}"
|
330
|
+
|
331
|
+
self.send(Command.SAVE_METHOD_CMD)
|
332
|
+
self.send(Command.SAVE_SEQUENCE_CMD)
|
300
333
|
self.send(Command.RUN_SEQUENCE_CMD.value)
|
301
334
|
self.timeout = total_runtime * 60
|
302
335
|
|
@@ -308,20 +341,12 @@ class SequenceController(RunController):
|
|
308
341
|
break
|
309
342
|
|
310
343
|
if hplc_running:
|
311
|
-
full_path_name, current_sample_file =
|
312
|
-
for _ in range(5):
|
313
|
-
try:
|
314
|
-
full_path_name, current_sample_file = (
|
315
|
-
self.get_current_run_data_dir_file()
|
316
|
-
)
|
317
|
-
break
|
318
|
-
except ValueError:
|
319
|
-
pass
|
344
|
+
full_path_name, current_sample_file = self.try_getting_run_info(folder_name)
|
320
345
|
if full_path_name and current_sample_file:
|
321
346
|
data_file = SequenceDataFiles(
|
322
347
|
sequence_name=self.table_state.name,
|
323
348
|
dir=full_path_name,
|
324
|
-
|
349
|
+
child_dirs=[os.path.join(full_path_name, current_sample_file)],
|
325
350
|
)
|
326
351
|
self.data_files.append(data_file)
|
327
352
|
else:
|
@@ -332,42 +357,38 @@ class SequenceController(RunController):
|
|
332
357
|
if run_completed.is_ok():
|
333
358
|
self.data_files[-1] = run_completed.ok_value
|
334
359
|
else:
|
335
|
-
warnings.warn(
|
360
|
+
warnings.warn(run_completed.err_value)
|
336
361
|
else:
|
337
362
|
raise RuntimeError("Sequence run may not have started.")
|
338
363
|
|
364
|
+
def try_getting_run_info(self, folder_name: str) -> Tuple[str, str | None]:
|
365
|
+
full_path_name, current_sample_file = None, None
|
366
|
+
for _ in range(5):
|
367
|
+
try:
|
368
|
+
full_path_name, current_sample_file = (
|
369
|
+
self.get_current_run_data_dir_file()
|
370
|
+
)
|
371
|
+
except ValueError:
|
372
|
+
pass
|
373
|
+
if current_sample_file and full_path_name:
|
374
|
+
return full_path_name, current_sample_file
|
375
|
+
elif full_path_name:
|
376
|
+
return full_path_name, None
|
377
|
+
raise ValueError("Could not get sequence data folder")
|
378
|
+
|
339
379
|
@override
|
340
|
-
def
|
341
|
-
self, most_recent_folder: T
|
380
|
+
def _fuzzy_match_most_recent_folder(
|
381
|
+
self, most_recent_folder: T
|
342
382
|
) -> Result[SequenceDataFiles, str]:
|
343
383
|
if isinstance(most_recent_folder, SequenceDataFiles):
|
344
384
|
try:
|
345
385
|
if most_recent_folder.dir and os.path.isdir(most_recent_folder.dir):
|
346
386
|
subdirs = [x[0] for x in os.walk(most_recent_folder.dir)]
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
most_recent_folder.child_dirs = [
|
353
|
-
os.path.join(most_recent_folder.dir, f) for f in child_dirs
|
354
|
-
]
|
355
|
-
else:
|
356
|
-
potential_folders: List[str] = sorted(
|
357
|
-
list(
|
358
|
-
filter(
|
359
|
-
lambda d: most_recent_folder.dir in d,
|
360
|
-
subdirs,
|
361
|
-
)
|
362
|
-
)
|
363
|
-
)
|
364
|
-
most_recent_folder.child_dirs = [
|
365
|
-
f
|
366
|
-
for f in potential_folders
|
367
|
-
if most_recent_folder.dir in f
|
368
|
-
and ".M" not in f
|
369
|
-
and ".D" in f
|
370
|
-
]
|
387
|
+
most_recent_folder.child_dirs = [
|
388
|
+
f
|
389
|
+
for f in subdirs
|
390
|
+
if most_recent_folder.dir in f and ".D" in f and f[-1] == "D"
|
391
|
+
]
|
371
392
|
return Ok(most_recent_folder)
|
372
393
|
else:
|
373
394
|
return Err("No sequence folder found, please give the full path.")
|
@@ -382,14 +403,11 @@ class SequenceController(RunController):
|
|
382
403
|
if custom_path
|
383
404
|
else self.data_files[-1]
|
384
405
|
)
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
seq_data_dir = search_folder.ok_value
|
391
|
-
else:
|
392
|
-
raise FileNotFoundError(search_folder.err_value)
|
406
|
+
search_folder = self._fuzzy_match_most_recent_folder(seq_data_dir)
|
407
|
+
if search_folder.is_ok():
|
408
|
+
seq_data_dir = search_folder.ok_value
|
409
|
+
else:
|
410
|
+
raise FileNotFoundError(search_folder.err_value)
|
393
411
|
all_w_spectra: List[Dict[int, AgilentHPLCChromatogram]] = []
|
394
412
|
for row in seq_data_dir.child_dirs:
|
395
413
|
all_w_spectra.append(self.get_data_uv(custom_path=row))
|
@@ -413,10 +431,9 @@ class SequenceController(RunController):
|
|
413
431
|
if custom_path
|
414
432
|
else self.data_files[-1]
|
415
433
|
)
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
).ok_value
|
434
|
+
self.data_files[-1] = self._fuzzy_match_most_recent_folder(
|
435
|
+
seq_file_dir
|
436
|
+
).ok_value
|
420
437
|
spectra: List[AgilentChannelChromatogramData] = []
|
421
438
|
for row in self.data_files[-1].child_dirs:
|
422
439
|
self.get_spectrum_at_channels(row)
|
@@ -430,15 +447,18 @@ class SequenceController(RunController):
|
|
430
447
|
) -> List[AgilentReport]:
|
431
448
|
if custom_path:
|
432
449
|
self.data_files.append(
|
433
|
-
self.
|
450
|
+
self._fuzzy_match_most_recent_folder(
|
434
451
|
most_recent_folder=SequenceDataFiles(
|
435
452
|
dir=custom_path,
|
436
453
|
sequence_name="NA",
|
437
454
|
),
|
438
|
-
child_dirs=None,
|
439
455
|
).ok_value
|
440
456
|
)
|
441
457
|
parent_dir = self.data_files[-1]
|
458
|
+
parent_dir = self._fuzzy_match_most_recent_folder(
|
459
|
+
most_recent_folder=parent_dir,
|
460
|
+
).ok_value
|
461
|
+
assert len(parent_dir.child_dirs) != 0
|
442
462
|
spectra = self.get_data()
|
443
463
|
reports = []
|
444
464
|
for i, child_dir in enumerate(parent_dir.child_dirs):
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from ..abc_tables.device import DeviceController
|
4
3
|
from ....control.controllers import CommunicationController
|
4
|
+
from ....utils.abc_tables.device import DeviceController
|
5
5
|
from ....utils.injector_types import (
|
6
6
|
Draw,
|
7
7
|
Inject,
|
@@ -17,7 +17,7 @@ from ....utils.injector_types import (
|
|
17
17
|
)
|
18
18
|
from ....utils.macro import Response
|
19
19
|
from ....utils.table_types import RegisterFlag, Table
|
20
|
-
from ....utils.tray_types import Tray, FiftyFourVialPlate,
|
20
|
+
from ....utils.tray_types import Tray, FiftyFourVialPlate, VialBar, LocationPlus
|
21
21
|
|
22
22
|
|
23
23
|
class InjectorController(DeviceController):
|
@@ -31,7 +31,7 @@ class InjectorController(DeviceController):
|
|
31
31
|
return FiftyFourVialPlate.from_str(val)
|
32
32
|
except Exception:
|
33
33
|
try:
|
34
|
-
return
|
34
|
+
return VialBar(int(val))
|
35
35
|
except Exception:
|
36
36
|
raise ValueError("Location could not be identified.")
|
37
37
|
|
pychemstation/control/hplc.py
CHANGED
@@ -20,7 +20,7 @@ from ..control.controllers import CommunicationController
|
|
20
20
|
from ..utils.injector_types import InjectorTable
|
21
21
|
from ..utils.macro import Command, Response, Status
|
22
22
|
from ..utils.method_types import MethodDetails
|
23
|
-
from ..utils.sequence_types import SequenceTable
|
23
|
+
from ..utils.sequence_types import SequenceTable, SequenceDataFiles
|
24
24
|
from ..utils.table_types import Table
|
25
25
|
|
26
26
|
|
@@ -32,8 +32,6 @@ class HPLCController:
|
|
32
32
|
|
33
33
|
INJECTOR_TABLE = Table(register="RCWLS1Pretreatment[1]", name="InstructionTable")
|
34
34
|
|
35
|
-
DAD_TABLE = Table(register="RCDAD1Method[1]", name="Timetable")
|
36
|
-
|
37
35
|
MSD_TABLE = Table(register="MSACQINFO[1]", name="SprayChamber")
|
38
36
|
|
39
37
|
def __init__(
|
@@ -63,7 +61,7 @@ class HPLCController:
|
|
63
61
|
if extra_data_dirs:
|
64
62
|
data_dirs.extend(extra_data_dirs)
|
65
63
|
data_dirs = list(set([os.path.normpath(p) for p in data_dirs]))
|
66
|
-
if method_dir and sequence_dir and data_dirs:
|
64
|
+
if (method_dir and sequence_dir and data_dirs and not offline) or offline:
|
67
65
|
self.method_controller: MethodController = MethodController(
|
68
66
|
controller=self.comm,
|
69
67
|
src=method_dir,
|
@@ -82,14 +80,15 @@ class HPLCController:
|
|
82
80
|
method_controller=self.method_controller,
|
83
81
|
offline=offline,
|
84
82
|
)
|
85
|
-
|
83
|
+
elif not offline and (not method_dir or not sequence_dir or not data_dirs):
|
86
84
|
raise ValueError(
|
87
|
-
"Expected a method dir, sequence dir and data dirs but
|
85
|
+
f"Expected a method dir: {method_dir}, sequence dir: {sequence_dir} and data dirs:{data_dirs} but one was None."
|
88
86
|
)
|
87
|
+
else:
|
88
|
+
raise ValueError("Expected error occured, please try again.")
|
89
89
|
|
90
90
|
def send(self, cmd: Union[Command, str]):
|
91
|
-
"""
|
92
|
-
Sends any Command or string to Chemstation.
|
91
|
+
"""Sends any Command or string to Chemstation.
|
93
92
|
|
94
93
|
:param cmd: the macro to send to Chemstation
|
95
94
|
"""
|
@@ -100,20 +99,22 @@ class HPLCController:
|
|
100
99
|
self.comm.send(cmd)
|
101
100
|
|
102
101
|
def receive(self) -> None | Response | str:
|
103
|
-
"""
|
104
|
-
Get the most recent response from Chemstation.
|
102
|
+
"""Get the most recent response from Chemstation.
|
105
103
|
|
106
|
-
:return: most recent response from
|
104
|
+
:return: most recent response from the most recently sent MACRO that returned a response.
|
107
105
|
"""
|
108
106
|
if not self.comm:
|
109
107
|
raise RuntimeError(
|
110
108
|
"Communication controller must be initialized before sending command. It is currently in offline mode."
|
111
109
|
)
|
112
|
-
|
110
|
+
res = self.comm.receive()
|
111
|
+
if res.is_ok():
|
112
|
+
return res.ok_value
|
113
|
+
else:
|
114
|
+
return res.err_value
|
113
115
|
|
114
116
|
def status(self) -> Status:
|
115
|
-
"""
|
116
|
-
Get the current status of the HPLC machine.
|
117
|
+
"""Get the current status of the HPLC machine.
|
117
118
|
|
118
119
|
:return: current status of the HPLC machine; Status types can be found in `pychemstation.utils.macro`
|
119
120
|
"""
|
@@ -124,8 +125,7 @@ class HPLCController:
|
|
124
125
|
return self.comm.get_status()
|
125
126
|
|
126
127
|
def switch_method(self, method_name: str):
|
127
|
-
"""
|
128
|
-
Allows the user to switch between pre-programmed methods. No need to append '.M'
|
128
|
+
"""Allows the user to switch between pre-programmed methods. No need to append '.M'
|
129
129
|
to the end of the method name. For example. for the method named 'General-Poroshell.M',
|
130
130
|
only 'General-Poroshell' is needed.
|
131
131
|
|
@@ -136,8 +136,7 @@ class HPLCController:
|
|
136
136
|
self.method_controller.switch(method_name)
|
137
137
|
|
138
138
|
def switch_sequence(self, sequence_name: str):
|
139
|
-
"""
|
140
|
-
Allows the user to switch between pre-programmed sequences. The sequence name does not need the '.S' extension.
|
139
|
+
"""Allows the user to switch between pre-programmed sequences. The sequence name does not need the '.S' extension.
|
141
140
|
For example: for the method named 'mySeq.S', only 'mySeq' is needed.
|
142
141
|
|
143
142
|
:param sequence_name: The name of the sequence file
|
@@ -150,8 +149,7 @@ class HPLCController:
|
|
150
149
|
add_timestamp: bool = True,
|
151
150
|
stall_while_running: bool = True,
|
152
151
|
):
|
153
|
-
"""
|
154
|
-
This is the preferred method to trigger a run.
|
152
|
+
"""This is the preferred method to trigger a run.
|
155
153
|
Starts the currently selected method, storing data
|
156
154
|
under the <data_dir>/<experiment_name>.D folder.
|
157
155
|
Device must be ready.
|
@@ -171,8 +169,7 @@ class HPLCController:
|
|
171
169
|
self.method_controller.stop()
|
172
170
|
|
173
171
|
def run_sequence(self, stall_while_running: bool = True):
|
174
|
-
"""
|
175
|
-
Starts the currently loaded sequence, storing data
|
172
|
+
"""Starts the currently loaded sequence, storing data
|
176
173
|
under one of the data_dirs/<sequence table name> folder.
|
177
174
|
Device must be ready.
|
178
175
|
|
@@ -181,24 +178,21 @@ class HPLCController:
|
|
181
178
|
self.sequence_controller.run(stall_while_running=stall_while_running)
|
182
179
|
|
183
180
|
def check_method_complete(self) -> Tuple[float, int]:
|
184
|
-
"""
|
185
|
-
Check if the currently running method (if any) is done.
|
181
|
+
"""Check if the currently running method (if any) is done.
|
186
182
|
|
187
|
-
:returns the percent of the method run completed, and whether the run is complete.
|
183
|
+
:returns: the percent of the method run completed, and whether the run is complete.
|
188
184
|
"""
|
189
185
|
return self.method_controller.check_hplc_run_finished()
|
190
186
|
|
191
187
|
def check_sequence_complete(self) -> Tuple[float, int]:
|
192
|
-
"""
|
193
|
-
Check if the currently running sequence (if any) is done.
|
188
|
+
"""Check if the currently running sequence (if any) is done.
|
194
189
|
|
195
190
|
:return: the percent of the sequence run completed, and whether the run is complete.
|
196
191
|
"""
|
197
192
|
return self.sequence_controller.check_hplc_run_finished()
|
198
193
|
|
199
194
|
def edit_method(self, updated_method: MethodDetails, save: bool = False):
|
200
|
-
"""
|
201
|
-
Updated the currently loaded method in ChemStation with provided values.
|
195
|
+
"""Updated the currently loaded method in ChemStation with provided values.
|
202
196
|
|
203
197
|
:param updated_method: the method with updated values, to be sent to Chemstation to modify the currently loaded method.
|
204
198
|
:param save: whether this method should be saved to disk, or just modified.
|
@@ -206,20 +200,28 @@ class HPLCController:
|
|
206
200
|
self.method_controller.edit(updated_method, save)
|
207
201
|
|
208
202
|
def edit_sequence(self, updated_sequence: SequenceTable):
|
209
|
-
"""
|
210
|
-
Updates the currently loaded sequence table with the provided table, and saves the sequence.
|
203
|
+
"""Updates the currently loaded sequence table with the provided table, and saves the sequence.
|
211
204
|
|
212
205
|
:param updated_sequence: The sequence table to be written to the currently loaded sequence table.
|
213
206
|
"""
|
214
207
|
self.sequence_controller.edit(updated_sequence)
|
215
208
|
|
209
|
+
def get_last_run_method_file_path(self) -> str:
|
210
|
+
"""Get the folder (ending with .D) for last run method.
|
211
|
+
|
212
|
+
:return: Complete path for method run.
|
213
|
+
"""
|
214
|
+
if len(self.method_controller.data_files) > 0:
|
215
|
+
return self.method_controller.data_files[-1]
|
216
|
+
else:
|
217
|
+
raise UserWarning("No data yet!")
|
218
|
+
|
216
219
|
def get_last_run_method_report(
|
217
220
|
self,
|
218
221
|
custom_path: Optional[str] = None,
|
219
222
|
report_type: ReportType = ReportType.CSV,
|
220
223
|
) -> AgilentReport:
|
221
|
-
"""
|
222
|
-
Return data contained in the REPORT files. Use `aghplctools` if you want more report processing utility.
|
224
|
+
"""Return data contained in the REPORT files. Use `aghplctools` if you want more report processing utility.
|
223
225
|
|
224
226
|
:param custom_path: path to sequence folder
|
225
227
|
:param report_type: read either the TXT or CSV version
|
@@ -232,8 +234,7 @@ class HPLCController:
|
|
232
234
|
def get_last_run_method_data(
|
233
235
|
self, read_uv: bool = False, custom_path: Optional[str] = None
|
234
236
|
) -> Dict[int, AgilentHPLCChromatogram] | AgilentChannelChromatogramData:
|
235
|
-
"""
|
236
|
-
Returns the last run method data.
|
237
|
+
"""Returns the last run method data.
|
237
238
|
|
238
239
|
:param custom_path: If you want to just load method data but from a file path. This file path must be the complete file path.
|
239
240
|
:param read_uv: whether to also read the UV file
|
@@ -243,13 +244,25 @@ class HPLCController:
|
|
243
244
|
else:
|
244
245
|
return self.method_controller.get_data(custom_path=custom_path)
|
245
246
|
|
247
|
+
def get_last_run_sequence_file_paths(self) -> SequenceDataFiles:
|
248
|
+
"""Get the sequence folder and all run folders (ending with .D).
|
249
|
+
|
250
|
+
:return: `SequenceDataFiles` containing complete path locations for sequence folder and all runs.
|
251
|
+
"""
|
252
|
+
if len(self.sequence_controller.data_files):
|
253
|
+
self.sequence_controller._fuzzy_match_most_recent_folder(
|
254
|
+
most_recent_folder=self.sequence_controller.data_files[-1],
|
255
|
+
)
|
256
|
+
return self.sequence_controller.data_files[-1]
|
257
|
+
else:
|
258
|
+
raise UserWarning("No data files yet!")
|
259
|
+
|
246
260
|
def get_last_run_sequence_reports(
|
247
261
|
self,
|
248
262
|
custom_path: Optional[str] = None,
|
249
263
|
report_type: ReportType = ReportType.CSV,
|
250
264
|
) -> List[AgilentReport]:
|
251
|
-
"""
|
252
|
-
Return data contained in the REPORT files. Use `aghplctools` if you want more report processing utility.
|
265
|
+
"""Return data contained in the REPORT files. Use `aghplctools` if you want more report processing utility.
|
253
266
|
|
254
267
|
:param custom_path: path to sequence folder
|
255
268
|
:param report_type: read either the TXT or CSV version
|
@@ -264,8 +277,7 @@ class HPLCController:
|
|
264
277
|
) -> (
|
265
278
|
List[Dict[int, AgilentHPLCChromatogram]] | List[AgilentChannelChromatogramData]
|
266
279
|
):
|
267
|
-
"""
|
268
|
-
Returns data for all rows in the last run sequence data.
|
280
|
+
"""Returns data for all rows in the last run sequence data.
|
269
281
|
|
270
282
|
:param custom_path: If you want to just load sequence data but from a file path. This file path must be the complete file path.
|
271
283
|
:param read_uv: whether to also read the UV file
|
@@ -277,11 +289,11 @@ class HPLCController:
|
|
277
289
|
|
278
290
|
def check_loaded_sequence(self) -> str:
|
279
291
|
"""Returns the name of the currently loaded sequence."""
|
280
|
-
return self.sequence_controller.
|
292
|
+
return self.sequence_controller.get_current_sequence_name()
|
281
293
|
|
282
294
|
def check_loaded_method(self) -> str:
|
283
295
|
"""Returns the name of the currently loaded method."""
|
284
|
-
return self.method_controller.
|
296
|
+
return self.method_controller.get_current_method_name()
|
285
297
|
|
286
298
|
def load_method(self) -> MethodDetails:
|
287
299
|
"""Returns details of the currently loaded method, such as its starting modifier conditions and timetable."""
|
pychemstation/utils/__init__.py
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
from . import abc_tables
|
2
|
+
from . import injector_types
|
3
|
+
from . import macro
|
4
|
+
from . import method_types
|
5
|
+
from . import num_utils
|
6
|
+
from . import parsing
|
7
|
+
from . import sequence_types
|
8
|
+
from . import spec_utils
|
9
|
+
from . import table_types
|
10
|
+
from . import tray_types
|
11
|
+
|
12
|
+
__all__ = [
|
13
|
+
"abc_tables",
|
14
|
+
"injector_types",
|
15
|
+
"macro",
|
16
|
+
"method_types",
|
17
|
+
"num_utils",
|
18
|
+
"parsing",
|
19
|
+
"sequence_types",
|
20
|
+
"spec_utils",
|
21
|
+
"table_types",
|
22
|
+
"tray_types",
|
23
|
+
]
|