pychemstation 0.6.6__tar.gz → 0.7.0.dev2__tar.gz
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-0.6.6 → pychemstation-0.7.0.dev2}/PKG-INFO +2 -1
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/devices/injector.py +2 -2
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/tables/method.py +14 -13
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/tables/sequence.py +29 -19
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/tables/table.py +24 -31
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/hplc.py +7 -8
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation.egg-info/PKG-INFO +2 -1
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation.egg-info/requires.txt +1 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/setup.py +3 -2
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/tests/constants.py +9 -5
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/tests/test_runs_stable.py +42 -5
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/tests/test_stable.py +17 -9
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/LICENSE +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/README.md +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/__init__.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/analysis/__init__.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/analysis/base_spectrum.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/analysis/process_report.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/analysis/spec_utils.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/analysis/utils.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/__init__.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/__init__.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/comm.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/devices/__init__.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/devices/column.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/devices/dad.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/devices/device.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/devices/pump.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/tables/__init__.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/tables/ms.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/generated/__init__.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/generated/dad_method.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/generated/pump_method.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/utils/__init__.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/utils/chromatogram.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/utils/injector_types.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/utils/macro.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/utils/method_types.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/utils/parsing.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/utils/pump_types.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/utils/sequence_types.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/utils/table_types.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/utils/tray_types.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation.egg-info/SOURCES.txt +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation.egg-info/dependency_links.txt +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation.egg-info/top_level.txt +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pyproject.toml +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/setup.cfg +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/tests/__init__.py +0 -0
- {pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/tests/test_nightly.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pychemstation
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.0.dev2
|
4
4
|
Summary: Library to interact with Chemstation software, primarily used in Hein lab
|
5
5
|
Home-page: https://gitlab.com/heingroup/device-api/pychemstation
|
6
6
|
Author: Lucy Hao
|
@@ -15,6 +15,7 @@ Requires-Dist: seabreeze
|
|
15
15
|
Requires-Dist: xsdata
|
16
16
|
Requires-Dist: result
|
17
17
|
Requires-Dist: rainbow-api
|
18
|
+
Requires-Dist: aghplctools==4.8.6
|
18
19
|
|
19
20
|
# Agilent HPLC Macro Control
|
20
21
|
|
@@ -44,9 +44,9 @@ class InjectorController(DeviceController):
|
|
44
44
|
nonlocal columns_added
|
45
45
|
if True:
|
46
46
|
if isinstance(val, str):
|
47
|
-
self.
|
47
|
+
self._edit_row_text(col_name=col_name, val=val)
|
48
48
|
else:
|
49
|
-
self.
|
49
|
+
self._edit_row_num(col_name=col_name, val=val)
|
50
50
|
else:
|
51
51
|
if isinstance(val, str):
|
52
52
|
self.add_new_col_text(col_name=col_name, val=val)
|
{pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/tables/method.py
RENAMED
@@ -1,5 +1,6 @@
|
|
1
1
|
import os
|
2
2
|
import time
|
3
|
+
from typing import List
|
3
4
|
|
4
5
|
from xsdata.formats.dataclass.parsers import XmlParser
|
5
6
|
|
@@ -20,12 +21,12 @@ class MethodController(TableController):
|
|
20
21
|
|
21
22
|
def __init__(self, controller: CommunicationController,
|
22
23
|
src: str,
|
23
|
-
|
24
|
+
data_dirs: List[str],
|
24
25
|
table: Table,
|
25
26
|
offline: bool,
|
26
27
|
injector_controller: InjectorController):
|
27
28
|
self.injector_controller = injector_controller
|
28
|
-
super().__init__(controller, src,
|
29
|
+
super().__init__(controller, src, data_dirs, table, offline=offline)
|
29
30
|
|
30
31
|
def check(self) -> str:
|
31
32
|
time.sleep(2)
|
@@ -133,7 +134,7 @@ class MethodController(TableController):
|
|
133
134
|
:raise AssertionError: The desired method is not selected. Try again.
|
134
135
|
"""
|
135
136
|
self.send(Command.SWITCH_METHOD_CMD_SPECIFIC.value.format(method_dir=self.src,
|
136
|
-
|
137
|
+
method_name=method_name))
|
137
138
|
|
138
139
|
time.sleep(2)
|
139
140
|
self.send(Command.GET_METHOD_CMD)
|
@@ -266,24 +267,24 @@ class MethodController(TableController):
|
|
266
267
|
if row.flow:
|
267
268
|
self.add_row()
|
268
269
|
self.get_num_rows()
|
269
|
-
self.
|
270
|
+
self._edit_row_text(col_name=RegisterFlag.FUNCTION, val=RegisterFlag.FLOW.value)
|
270
271
|
self.add_new_col_num(col_name=RegisterFlag.TIMETABLE_FLOW, val=row.flow)
|
271
|
-
self.
|
272
|
+
self._edit_row_num(col_name=RegisterFlag.TIMETABLE_FLOW, val=row.flow)
|
272
273
|
self.download()
|
273
274
|
else:
|
274
275
|
if row.organic_modifer:
|
275
276
|
self.add_row()
|
276
277
|
self.get_num_rows()
|
277
|
-
self.
|
278
|
-
self.
|
279
|
-
self.
|
278
|
+
self._edit_row_text(col_name=RegisterFlag.FUNCTION, val=RegisterFlag.SOLVENT_COMPOSITION.value)
|
279
|
+
self._edit_row_num(col_name=RegisterFlag.TIME, val=row.start_time)
|
280
|
+
self._edit_row_num(col_name=RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION, val=row.organic_modifer)
|
280
281
|
self.download()
|
281
282
|
if row.flow:
|
282
283
|
self.add_row()
|
283
284
|
self.get_num_rows()
|
284
|
-
self.
|
285
|
-
self.
|
286
|
-
self.
|
285
|
+
self._edit_row_text(col_name=RegisterFlag.FUNCTION, val=RegisterFlag.FLOW.value)
|
286
|
+
self._edit_row_num(col_name=RegisterFlag.TIMETABLE_FLOW, val=row.flow)
|
287
|
+
self._edit_row_num(col_name=RegisterFlag.TIME, val=row.start_time)
|
287
288
|
self.download()
|
288
289
|
|
289
290
|
def _update_method_timetable(self, timetable_rows: list[TimeTableEntry]):
|
@@ -323,7 +324,7 @@ class MethodController(TableController):
|
|
323
324
|
tries = 0
|
324
325
|
while tries < 10 and not hplc_is_running:
|
325
326
|
timestamp = time.strftime(TIME_FORMAT)
|
326
|
-
self.send(Command.RUN_METHOD_CMD.value.format(data_dir=self.
|
327
|
+
self.send(Command.RUN_METHOD_CMD.value.format(data_dir=self.data_dirs[0],
|
327
328
|
experiment_name=experiment_name,
|
328
329
|
timestamp=timestamp))
|
329
330
|
folder_name = f"{experiment_name}_{timestamp}.D"
|
@@ -333,7 +334,7 @@ class MethodController(TableController):
|
|
333
334
|
if not hplc_is_running:
|
334
335
|
raise RuntimeError("Method failed to start.")
|
335
336
|
|
336
|
-
self.data_files.append(os.path.join(self.
|
337
|
+
self.data_files.append(os.path.join(self.data_dirs[0], folder_name))
|
337
338
|
|
338
339
|
if stall_while_running:
|
339
340
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import os
|
2
2
|
import time
|
3
|
-
from typing import Optional, Union
|
3
|
+
from typing import Optional, Union, List
|
4
4
|
|
5
5
|
from .table import TableController, ChromData
|
6
6
|
from ....control.controllers.comm import CommunicationController
|
@@ -16,10 +16,14 @@ class SequenceController(TableController):
|
|
16
16
|
Class containing sequence related logic
|
17
17
|
"""
|
18
18
|
|
19
|
-
def __init__(self, controller: CommunicationController,
|
19
|
+
def __init__(self, controller: CommunicationController,
|
20
|
+
src: str,
|
21
|
+
data_dirs: List[str],
|
22
|
+
table: Table,
|
23
|
+
method_dir: str,
|
20
24
|
offline: bool):
|
21
25
|
self.method_dir = method_dir
|
22
|
-
super().__init__(controller, src,
|
26
|
+
super().__init__(controller, src, data_dirs, table, offline=offline)
|
23
27
|
|
24
28
|
def load(self) -> SequenceTable:
|
25
29
|
rows = self.get_num_rows()
|
@@ -41,7 +45,8 @@ class SequenceController(TableController):
|
|
41
45
|
inj_vol = int(self.get_text(row, RegisterFlag.INJ_VOL))
|
42
46
|
inj_source = InjectionSource(self.get_text(row, RegisterFlag.INJ_SOR))
|
43
47
|
sample_type = SampleType(self.get_num(row, RegisterFlag.SAMPLE_TYPE))
|
44
|
-
vial_enum = TenVialColumn(vial_location) if vial_location <= 10 else FiftyFourVialPlate.from_int(
|
48
|
+
vial_enum = TenVialColumn(vial_location) if vial_location <= 10 else FiftyFourVialPlate.from_int(
|
49
|
+
num=vial_location)
|
45
50
|
return SequenceEntry(sample_name=sample_name,
|
46
51
|
vial_location=vial_enum,
|
47
52
|
method=None if len(method) == 0 else method,
|
@@ -98,12 +103,12 @@ class SequenceController(TableController):
|
|
98
103
|
self.send(Command.SWITCH_SEQUENCE_CMD)
|
99
104
|
|
100
105
|
for i, row in enumerate(sequence_table.rows):
|
101
|
-
self.
|
106
|
+
self._edit_row(row=row, row_num=i + 1)
|
102
107
|
self.sleep(1)
|
103
108
|
self.send(Command.SAVE_SEQUENCE_CMD)
|
104
109
|
self.send(Command.SWITCH_SEQUENCE_CMD)
|
105
110
|
|
106
|
-
def
|
111
|
+
def _edit_row(self, row: SequenceEntry, row_num: int):
|
107
112
|
"""
|
108
113
|
Edits a row in the sequence table. If a row does NOT exist, a new one will be created.
|
109
114
|
|
@@ -123,33 +128,33 @@ class SequenceController(TableController):
|
|
123
128
|
loc = row.vial_location.value
|
124
129
|
elif isinstance(loc, FiftyFourVialPlate):
|
125
130
|
loc = row.vial_location.value()
|
126
|
-
self.
|
131
|
+
self._edit_row_num(row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc)
|
127
132
|
|
128
133
|
if row.method:
|
129
134
|
possible_path = os.path.join(self.method_dir, row.method) + ".M\\"
|
130
135
|
method = row.method
|
131
136
|
if os.path.exists(possible_path):
|
132
137
|
method = os.path.join(self.method_dir, row.method)
|
133
|
-
self.
|
138
|
+
self._edit_row_text(row=row_num, col_name=RegisterFlag.METHOD, val=method)
|
134
139
|
|
135
140
|
if row.num_inj:
|
136
|
-
self.
|
141
|
+
self._edit_row_num(row=row_num, col_name=RegisterFlag.NUM_INJ, val=row.num_inj)
|
137
142
|
|
138
143
|
if row.inj_vol:
|
139
|
-
self.
|
144
|
+
self._edit_row_text(row=row_num, col_name=RegisterFlag.INJ_VOL, val=row.inj_vol)
|
140
145
|
|
141
146
|
if row.inj_source:
|
142
|
-
self.
|
147
|
+
self._edit_row_text(row=row_num, col_name=RegisterFlag.INJ_SOR, val=row.inj_source.value)
|
143
148
|
|
144
149
|
if row.sample_name:
|
145
|
-
self.
|
150
|
+
self._edit_row_text(row=row_num, col_name=RegisterFlag.NAME, val=row.sample_name)
|
146
151
|
if row.data_file:
|
147
|
-
self.
|
152
|
+
self._edit_row_text(row=row_num, col_name=RegisterFlag.DATA_FILE, val=row.data_file)
|
148
153
|
else:
|
149
|
-
self.
|
154
|
+
self._edit_row_text(row=row_num, col_name=RegisterFlag.DATA_FILE, val=row.sample_name)
|
150
155
|
|
151
156
|
if row.sample_type:
|
152
|
-
self.
|
157
|
+
self._edit_row_num(row=row_num, col_name=RegisterFlag.SAMPLE_TYPE, val=row.sample_type.value)
|
153
158
|
|
154
159
|
self.send(Command.SAVE_SEQUENCE_CMD)
|
155
160
|
|
@@ -159,6 +164,7 @@ class SequenceController(TableController):
|
|
159
164
|
under the <data_dir>/<sequence table name> folder.
|
160
165
|
Device must be ready.
|
161
166
|
"""
|
167
|
+
self.controller.send(Command.SAVE_METHOD_CMD)
|
162
168
|
if not self.table_state:
|
163
169
|
self.table_state = self.load()
|
164
170
|
|
@@ -186,11 +192,15 @@ class SequenceController(TableController):
|
|
186
192
|
def get_data(self, custom_path: Optional[str] = None,
|
187
193
|
read_uv: bool = False) -> list[AgilentChannelChromatogramData]:
|
188
194
|
parent_dir = self.data_files[-1].dir if not custom_path else custom_path
|
189
|
-
subdirs = [x[0] for x in os.walk(self.data_dir)]
|
190
|
-
potential_folders = sorted(list(filter(lambda d: parent_dir in d, subdirs)))
|
191
|
-
self.data_files[-1].child_dirs = [f for f in potential_folders if
|
192
|
-
parent_dir in f and ".M" not in f and ".D" in f]
|
193
195
|
|
196
|
+
potential_folders = []
|
197
|
+
for d in self.data_dirs:
|
198
|
+
subdirs = [x[0] for x in os.walk(d)]
|
199
|
+
potential_folders = sorted(list(filter(lambda d: parent_dir in d, subdirs)))
|
200
|
+
if len(potential_folders) > 0:
|
201
|
+
break
|
202
|
+
assert len(potential_folders) > 0
|
203
|
+
self.data_files[-1].child_dirs = [f for f in potential_folders if parent_dir in f and ".M" not in f and ".D" in f]
|
194
204
|
spectra: list[Union[AgilentChannelChromatogramData, ChromData]] = []
|
195
205
|
all_w_spectra: list[Union[AgilentChannelChromatogramData, ChromData]] = []
|
196
206
|
for row in self.data_files[-1].child_dirs:
|
{pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/tables/table.py
RENAMED
@@ -8,7 +8,7 @@ import abc
|
|
8
8
|
import os
|
9
9
|
import warnings
|
10
10
|
from dataclasses import dataclass
|
11
|
-
from typing import Union, Optional, AnyStr
|
11
|
+
from typing import Union, Optional, AnyStr, List
|
12
12
|
|
13
13
|
import numpy as np
|
14
14
|
import polling
|
@@ -35,7 +35,7 @@ class TableController(abc.ABC):
|
|
35
35
|
|
36
36
|
def __init__(self, controller: CommunicationController,
|
37
37
|
src: Optional[str],
|
38
|
-
|
38
|
+
data_dirs: Optional[List[str]],
|
39
39
|
table: Table,
|
40
40
|
offline: bool = False):
|
41
41
|
self.controller = controller
|
@@ -51,23 +51,12 @@ class TableController(abc.ABC):
|
|
51
51
|
elif isinstance(src, str):
|
52
52
|
raise FileNotFoundError(f"dir: {src} not found.")
|
53
53
|
|
54
|
-
if
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
self.spectra: dict[str, Optional[AgilentHPLCChromatogram]] = {
|
61
|
-
"A": AgilentHPLCChromatogram(self.data_dir),
|
62
|
-
"B": AgilentHPLCChromatogram(self.data_dir),
|
63
|
-
"C": AgilentHPLCChromatogram(self.data_dir),
|
64
|
-
"D": AgilentHPLCChromatogram(self.data_dir),
|
65
|
-
"E": AgilentHPLCChromatogram(self.data_dir),
|
66
|
-
"F": AgilentHPLCChromatogram(self.data_dir),
|
67
|
-
"G": AgilentHPLCChromatogram(self.data_dir),
|
68
|
-
"H": AgilentHPLCChromatogram(self.data_dir),
|
69
|
-
}
|
70
|
-
else:
|
54
|
+
if data_dirs:
|
55
|
+
for d in data_dirs:
|
56
|
+
if not os.path.isdir(d):
|
57
|
+
raise FileNotFoundError(f"dir: {d} not found.")
|
58
|
+
self.data_dirs: List[str] = data_dirs
|
59
|
+
|
71
60
|
self.spectra: dict[str, Optional[AgilentHPLCChromatogram]] = {
|
72
61
|
"A": AgilentHPLCChromatogram(),
|
73
62
|
"B": AgilentHPLCChromatogram(),
|
@@ -79,7 +68,6 @@ class TableController(abc.ABC):
|
|
79
68
|
"H": AgilentHPLCChromatogram(),
|
80
69
|
}
|
81
70
|
self.data_files: Union[list[SequenceDataFiles], list[str]] = []
|
82
|
-
|
83
71
|
self.uv = None
|
84
72
|
|
85
73
|
def receive(self) -> Result[Response, str]:
|
@@ -137,10 +125,10 @@ class TableController(abc.ABC):
|
|
137
125
|
col_name=col_name,
|
138
126
|
val=val))
|
139
127
|
|
140
|
-
def
|
141
|
-
|
142
|
-
|
143
|
-
|
128
|
+
def _edit_row_num(self,
|
129
|
+
col_name: RegisterFlag,
|
130
|
+
val: Union[int, float],
|
131
|
+
row: Optional[int] = None):
|
144
132
|
self.sleepy_send(TableOperation.EDIT_ROW_VAL.value.format(
|
145
133
|
register=self.table.register,
|
146
134
|
table_name=self.table.name,
|
@@ -148,10 +136,10 @@ class TableController(abc.ABC):
|
|
148
136
|
col_name=col_name,
|
149
137
|
val=val))
|
150
138
|
|
151
|
-
def
|
152
|
-
|
153
|
-
|
154
|
-
|
139
|
+
def _edit_row_text(self,
|
140
|
+
col_name: RegisterFlag,
|
141
|
+
val: str,
|
142
|
+
row: Optional[int] = None):
|
155
143
|
self.sleepy_send(TableOperation.EDIT_ROW_TEXT.value.format(
|
156
144
|
register=self.table.register,
|
157
145
|
table_name=self.table.name,
|
@@ -270,8 +258,13 @@ class TableController(abc.ABC):
|
|
270
258
|
if os.path.exists(most_recent_folder):
|
271
259
|
return Ok(most_recent_folder)
|
272
260
|
|
273
|
-
|
274
|
-
|
261
|
+
potential_folders = []
|
262
|
+
for d in self.data_dirs:
|
263
|
+
subdirs = [x[0] for x in os.walk(d)]
|
264
|
+
potential_folders = sorted(list(filter(lambda d: most_recent_folder in d, subdirs)))
|
265
|
+
if len(potential_folders) > 0:
|
266
|
+
break
|
267
|
+
assert len(potential_folders) > 0
|
275
268
|
parent_dirs = []
|
276
269
|
for folder in potential_folders:
|
277
270
|
path = os.path.normpath(folder)
|
@@ -305,5 +298,5 @@ class TableController(abc.ABC):
|
|
305
298
|
try:
|
306
299
|
spec.load_spectrum(data_path=data_path, channel=channel)
|
307
300
|
except FileNotFoundError:
|
308
|
-
self.spectra[channel] = AgilentHPLCChromatogram(
|
301
|
+
self.spectra[channel] = AgilentHPLCChromatogram()
|
309
302
|
print(f"No data at channel: {channel}")
|
@@ -4,7 +4,7 @@ Module to provide API for higher-level HPLC actions.
|
|
4
4
|
Authors: Lucy Hao
|
5
5
|
"""
|
6
6
|
|
7
|
-
from typing import Union, Optional
|
7
|
+
from typing import Union, Optional, List
|
8
8
|
|
9
9
|
from .controllers.devices.injector import InjectorController
|
10
10
|
from ..control.controllers import MethodController, SequenceController, CommunicationController
|
@@ -40,25 +40,24 @@ class HPLCController:
|
|
40
40
|
|
41
41
|
def __init__(self,
|
42
42
|
comm_dir: str,
|
43
|
-
data_dir: str,
|
44
43
|
method_dir: str,
|
45
44
|
sequence_dir: str,
|
46
|
-
|
45
|
+
data_dirs: List[str],
|
47
46
|
offline: bool = False):
|
48
47
|
"""Initialize HPLC controller. The `hplc_talk.mac` macro file must be loaded in the Chemstation software.
|
49
48
|
`comm_dir` must match the file path in the macro file.
|
50
49
|
|
51
50
|
:param comm_dir: Name of directory for communication, where ChemStation will read and write from. Can be any existing directory.
|
52
|
-
:param
|
51
|
+
:param data_dirs: Name of directories for storing data after method or sequence runs. Method data dir is default
|
52
|
+
the first one in the list. In other words, the first dir in the list is highest prio.
|
53
53
|
:param method_dir: Name of directory where method files are stored.
|
54
54
|
:param sequence_dir: Name of directory where sequence files are stored.
|
55
|
-
:param sequence_data_dir: Name of directory for storing sequence data, if different from method
|
56
55
|
:raises FileNotFoundError: If either `data_dir`, `method_dir`, `sequence_dir`, `sequence_data_dir`or `comm_dir` is not a valid directory.
|
57
56
|
"""
|
58
57
|
self.comm = CommunicationController(comm_dir=comm_dir) if not offline else None
|
59
58
|
self.method_controller = MethodController(controller=self.comm,
|
60
59
|
src=method_dir,
|
61
|
-
|
60
|
+
data_dirs=data_dirs,
|
62
61
|
table=self.METHOD_TIMETABLE,
|
63
62
|
offline=offline,
|
64
63
|
injector_controller=InjectorController(controller=self.comm,
|
@@ -66,7 +65,7 @@ class HPLCController:
|
|
66
65
|
offline=offline))
|
67
66
|
self.sequence_controller = SequenceController(controller=self.comm,
|
68
67
|
src=sequence_dir,
|
69
|
-
|
68
|
+
data_dirs=data_dirs,
|
70
69
|
table=self.SEQUENCE_TABLE,
|
71
70
|
method_dir=method_dir,
|
72
71
|
offline=offline)
|
@@ -157,7 +156,7 @@ class HPLCController:
|
|
157
156
|
:param row: sequence row entry with updated information
|
158
157
|
:param num: the row to edit, based on 1-based indexing
|
159
158
|
"""
|
160
|
-
self.sequence_controller.
|
159
|
+
self.sequence_controller._edit_row(row, num)
|
161
160
|
|
162
161
|
def get_last_run_method_data(self, read_uv: bool = False,
|
163
162
|
data: Optional[str] = None) -> AgilentChannelChromatogramData:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pychemstation
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.0.dev2
|
4
4
|
Summary: Library to interact with Chemstation software, primarily used in Hein lab
|
5
5
|
Home-page: https://gitlab.com/heingroup/device-api/pychemstation
|
6
6
|
Author: Lucy Hao
|
@@ -15,6 +15,7 @@ Requires-Dist: seabreeze
|
|
15
15
|
Requires-Dist: xsdata
|
16
16
|
Requires-Dist: result
|
17
17
|
Requires-Dist: rainbow-api
|
18
|
+
Requires-Dist: aghplctools==4.8.6
|
18
19
|
|
19
20
|
# Agilent HPLC Macro Control
|
20
21
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
|
|
5
5
|
|
6
6
|
setuptools.setup(
|
7
7
|
name="pychemstation",
|
8
|
-
version="0.
|
8
|
+
version="0.7.0.dev2",
|
9
9
|
author="Lucy Hao",
|
10
10
|
author_email="lhao03@student.ubc.ca",
|
11
11
|
description="Library to interact with Chemstation software, primarily used in Hein lab",
|
@@ -18,7 +18,8 @@ setuptools.setup(
|
|
18
18
|
'seabreeze',
|
19
19
|
'xsdata',
|
20
20
|
'result',
|
21
|
-
'rainbow-api'
|
21
|
+
'rainbow-api',
|
22
|
+
'aghplctools==4.8.6'
|
22
23
|
],
|
23
24
|
classifiers=[
|
24
25
|
"Programming Language :: Python :: 3",
|
@@ -10,26 +10,30 @@ DEFAULT_METHOD = "GENERAL-POROSHELL-OPT"
|
|
10
10
|
DEFAULT_SEQUENCE = "hplc_testing"
|
11
11
|
|
12
12
|
# CONSTANTS: paths only work in Hein group HPLC machine in room 242
|
13
|
-
DEFAULT_COMMAND_PATH = "C:\\Users\\User\\Desktop\\Lucy\\"
|
13
|
+
DEFAULT_COMMAND_PATH = "C:\\Users\\User\\Desktop\\Lucy\\hplc-method-optimization\\tests\\"
|
14
14
|
DEFAULT_METHOD_DIR = "C:\\ChemStation\\1\\Methods\\"
|
15
|
-
DATA_DIR = "C:\\Users\\Public\\Documents\\ChemStation\\
|
15
|
+
DATA_DIR = "C:\\Users\\Public\\Documents\\ChemStation\\2\\Data\\LC-BO\\"
|
16
16
|
SEQUENCE_DIR = "C:\\USERS\\PUBLIC\\DOCUMENTS\\CHEMSTATION\\3\\Sequence"
|
17
|
+
SEQUENCE_DATA_DIR = "C:\\Users\\Public\\Documents\\ChemStation\\3\\Data\\"
|
17
18
|
|
18
19
|
HEIN_LAB_CONSTANTS = [DEFAULT_COMMAND_PATH,
|
19
20
|
DEFAULT_METHOD_DIR,
|
20
21
|
DATA_DIR,
|
21
|
-
SEQUENCE_DIR
|
22
|
+
SEQUENCE_DIR,
|
23
|
+
SEQUENCE_DATA_DIR]
|
22
24
|
|
23
25
|
# these CONSTANTS work in rm 254
|
24
26
|
DEFAULT_COMMAND_PATH_254 = "D:\\\git_repositories\\\hplc_comm\\"
|
25
27
|
DEFAULT_METHOD_DIR_254 = "D:\\Chemstation\\1\\Methods\\"
|
26
28
|
DATA_DIR_254 = "D:\\Chemstation\\1\\Data\\LC BO\\"
|
27
29
|
SEQUENCE_DIR_254 = "C:\\1\\Sequence\\"
|
30
|
+
SEQUENCE_DATA_DIR_254 = "D:\\Chemstation\\1\\Data\\"
|
28
31
|
|
29
32
|
HEIN_LAB_CONSTANTS_254 = [DEFAULT_COMMAND_PATH_254,
|
30
33
|
DEFAULT_METHOD_DIR_254,
|
31
34
|
DATA_DIR_254,
|
32
|
-
SEQUENCE_DIR_254
|
35
|
+
SEQUENCE_DIR_254,
|
36
|
+
SEQUENCE_DATA_DIR_254]
|
33
37
|
|
34
38
|
|
35
39
|
def room(num: int):
|
@@ -77,7 +81,7 @@ def set_up_utils(num: int) -> HPLCController:
|
|
77
81
|
|
78
82
|
controller = HPLCController(comm_dir=path_constants[0],
|
79
83
|
method_dir=path_constants[1],
|
80
|
-
|
84
|
+
data_dirs=[path_constants[2], path_constants[4]],
|
81
85
|
sequence_dir=path_constants[3])
|
82
86
|
controller.send(Command.SAVE_METHOD_CMD.value.format(commit_msg="method saved by pychemstation"))
|
83
87
|
controller.send(Command.SAVE_SEQUENCE_CMD)
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
import unittest
|
3
2
|
|
4
3
|
from pychemstation.utils.tray_types import FiftyFourVialPlate, Plate, Letter, Num
|
@@ -9,7 +8,7 @@ run_too = True
|
|
9
8
|
|
10
9
|
class TestRunsStable(unittest.TestCase):
|
11
10
|
def setUp(self):
|
12
|
-
self.hplc_controller = set_up_utils(
|
11
|
+
self.hplc_controller = set_up_utils(242)
|
13
12
|
|
14
13
|
def test_run_method(self):
|
15
14
|
try:
|
@@ -53,7 +52,7 @@ class TestRunsStable(unittest.TestCase):
|
|
53
52
|
num_inj=1,
|
54
53
|
sample_type=SampleType.SAMPLE
|
55
54
|
)])
|
56
|
-
self.hplc_controller.edit_sequence(seq_table)
|
55
|
+
self.hplc_controller.edit_sequence(seq_table) # nvm no bug??
|
57
56
|
|
58
57
|
self.hplc_controller.method_controller.switch(DEFAULT_METHOD)
|
59
58
|
rand_method = MethodDetails(
|
@@ -80,9 +79,47 @@ class TestRunsStable(unittest.TestCase):
|
|
80
79
|
def test_run_sequence(self):
|
81
80
|
try:
|
82
81
|
self.hplc_controller.switch_sequence(sequence_name=DEFAULT_SEQUENCE)
|
82
|
+
seq_table = SequenceTable(
|
83
|
+
name=DEFAULT_SEQUENCE,
|
84
|
+
rows=[SequenceEntry(vial_location=FiftyFourVialPlate(plate=Plate.ONE, letter=Letter.A, num=Num.ONE),
|
85
|
+
sample_name="P1-A1",
|
86
|
+
method=DEFAULT_METHOD,
|
87
|
+
inj_source=InjectionSource.HIP_ALS,
|
88
|
+
inj_vol=0.5,
|
89
|
+
num_inj=1,
|
90
|
+
sample_type=SampleType.SAMPLE),
|
91
|
+
SequenceEntry(vial_location=FiftyFourVialPlate(plate=Plate.ONE, letter=Letter.A, num=Num.TWO),
|
92
|
+
sample_name="P1-A2",
|
93
|
+
method=DEFAULT_METHOD,
|
94
|
+
inj_source=InjectionSource.HIP_ALS,
|
95
|
+
inj_vol=0.5,
|
96
|
+
num_inj=1,
|
97
|
+
sample_type=SampleType.SAMPLE),
|
98
|
+
SequenceEntry(vial_location=FiftyFourVialPlate(plate=Plate.ONE, letter=Letter.A, num=Num.THREE),
|
99
|
+
sample_name="P1-A3",
|
100
|
+
method=DEFAULT_METHOD,
|
101
|
+
inj_source=InjectionSource.HIP_ALS,
|
102
|
+
inj_vol=0.5,
|
103
|
+
num_inj=1,
|
104
|
+
sample_type=SampleType.SAMPLE)])
|
105
|
+
self.hplc_controller.edit_sequence(seq_table)
|
106
|
+
self.hplc_controller.switch_method(method_name=DEFAULT_METHOD)
|
107
|
+
method = MethodDetails(
|
108
|
+
name=DEFAULT_METHOD,
|
109
|
+
params=HPLCMethodParams(
|
110
|
+
organic_modifier=5,
|
111
|
+
flow=0.65),
|
112
|
+
timetable=[TimeTableEntry(
|
113
|
+
start_time=3.50,
|
114
|
+
organic_modifer=100,
|
115
|
+
flow=0.65)],
|
116
|
+
stop_time=5,
|
117
|
+
post_time=1)
|
118
|
+
self.hplc_controller.edit_method(method)
|
83
119
|
self.hplc_controller.preprun()
|
84
120
|
self.hplc_controller.run_sequence()
|
85
|
-
|
86
|
-
self.assertTrue(len(
|
121
|
+
chroms = self.hplc_controller.get_last_run_sequence_data()
|
122
|
+
self.assertTrue(len(chroms) == 3)
|
87
123
|
except Exception:
|
88
124
|
self.fail("Failed")
|
125
|
+
|
@@ -8,7 +8,7 @@ from tests.constants import *
|
|
8
8
|
class TestStable(unittest.TestCase):
|
9
9
|
|
10
10
|
def setUp(self):
|
11
|
-
self.hplc_controller = set_up_utils(
|
11
|
+
self.hplc_controller = set_up_utils(242)
|
12
12
|
|
13
13
|
def test_status_check_standby(self):
|
14
14
|
self.hplc_controller.standby()
|
@@ -52,14 +52,6 @@ class TestStable(unittest.TestCase):
|
|
52
52
|
except Exception as e:
|
53
53
|
self.fail(f"Should not throw error: {e}")
|
54
54
|
|
55
|
-
def test_load_method_from_disk(self):
|
56
|
-
self.hplc_controller.switch_method(DEFAULT_METHOD)
|
57
|
-
try:
|
58
|
-
gp_mtd = self.hplc_controller.method_controller.load_from_disk(DEFAULT_METHOD)
|
59
|
-
self.assertTrue(gp_mtd.params.organic_modifier == 5)
|
60
|
-
except Exception as e:
|
61
|
-
self.fail(f"Should have not failed, {e}")
|
62
|
-
|
63
55
|
def test_edit_method(self):
|
64
56
|
self.hplc_controller.method_controller.switch(DEFAULT_METHOD)
|
65
57
|
new_method = MethodDetails(name=DEFAULT_METHOD + ".M",
|
@@ -263,6 +255,22 @@ class TestStable(unittest.TestCase):
|
|
263
255
|
for i in range(len(vial_locations)):
|
264
256
|
self.assertEqual(vial_locations[i], FiftyFourVialPlate.from_int(vial_locations[i].value()))
|
265
257
|
|
258
|
+
def test_get_last_run_sequence(self):
|
259
|
+
path = "C:\\Users\\Public\\Documents\\ChemStation\\3\\Data\\hplc_testing 2025-03-24 16-28-16"
|
260
|
+
folder_name = "hplc_testing 2025-03-24 16-28"
|
261
|
+
self.hplc_controller.sequence_controller.data_files.append(SequenceDataFiles(dir=folder_name,
|
262
|
+
sequence_name=DEFAULT_SEQUENCE))
|
263
|
+
try:
|
264
|
+
most_recent_folder = self.hplc_controller.sequence_controller.retrieve_recent_data_files()
|
265
|
+
check_folder = self.hplc_controller.sequence_controller.fuzzy_match_most_recent_folder(
|
266
|
+
most_recent_folder=most_recent_folder)
|
267
|
+
self.assertEqual(check_folder.ok_value, path)
|
268
|
+
self.hplc_controller.sequence_controller.data_files[-1].dir = check_folder.ok_value
|
269
|
+
chrom = self.hplc_controller.get_last_run_sequence_data()
|
270
|
+
self.assertTrue(chrom)
|
271
|
+
except Exception:
|
272
|
+
self.fail()
|
273
|
+
|
266
274
|
|
267
275
|
if __name__ == '__main__':
|
268
276
|
unittest.main()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/devices/column.py
RENAMED
File without changes
|
{pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/devices/dad.py
RENAMED
File without changes
|
{pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/devices/device.py
RENAMED
File without changes
|
{pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/devices/pump.py
RENAMED
File without changes
|
File without changes
|
{pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation/control/controllers/tables/ms.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{pychemstation-0.6.6 → pychemstation-0.7.0.dev2}/pychemstation.egg-info/dependency_links.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|