pychemstation 0.5.12__tar.gz → 0.5.13.dev1__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.5.12 → pychemstation-0.5.13.dev1}/PKG-INFO +3 -2
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/README.md +1 -1
- pychemstation-0.5.13.dev1/pychemstation/control/controllers/__init__.py +9 -0
- pychemstation-0.5.13.dev1/pychemstation/control/controllers/devices/column.py +12 -0
- pychemstation-0.5.13.dev1/pychemstation/control/controllers/devices/device.py +23 -0
- pychemstation-0.5.13.dev1/pychemstation/control/controllers/devices/pump.py +43 -0
- {pychemstation-0.5.12/pychemstation/control/controllers → pychemstation-0.5.13.dev1/pychemstation/control/controllers/tables}/method.py +90 -82
- {pychemstation-0.5.12/pychemstation/control/controllers → pychemstation-0.5.13.dev1/pychemstation/control/controllers/tables}/sequence.py +23 -14
- pychemstation-0.5.12/pychemstation/control/controllers/table_controller.py → pychemstation-0.5.13.dev1/pychemstation/control/controllers/tables/table.py +31 -10
- pychemstation-0.5.13.dev1/pychemstation/utils/__init__.py +0 -0
- pychemstation-0.5.13.dev1/pychemstation/utils/injector_types.py +30 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/utils/macro.py +1 -0
- pychemstation-0.5.13.dev1/pychemstation/utils/pump_types.py +7 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/utils/sequence_types.py +3 -2
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/utils/tray_types.py +4 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation.egg-info/PKG-INFO +3 -2
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation.egg-info/SOURCES.txt +11 -3
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation.egg-info/requires.txt +1 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pyproject.toml +3 -1
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/setup.py +3 -2
- pychemstation-0.5.13.dev1/tests/__init__.py +0 -0
- pychemstation-0.5.13.dev1/tests/test_inj.py +38 -0
- pychemstation-0.5.12/pychemstation/control/controllers/__init__.py +0 -4
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/LICENSE +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/__init__.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/analysis/__init__.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/analysis/base_spectrum.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/analysis/spec_utils.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/analysis/utils.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/control/__init__.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/control/controllers/comm.py +0 -0
- {pychemstation-0.5.12/pychemstation/utils → pychemstation-0.5.13.dev1/pychemstation/control/controllers/devices}/__init__.py +0 -0
- {pychemstation-0.5.12/tests → pychemstation-0.5.13.dev1/pychemstation/control/controllers/tables}/__init__.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/control/hplc.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/generated/__init__.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/generated/dad_method.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/generated/pump_method.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/utils/chromatogram.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/utils/method_types.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/utils/parsing.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/utils/table_types.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation.egg-info/dependency_links.txt +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation.egg-info/top_level.txt +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/setup.cfg +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/tests/constants.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/tests/test_comb.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/tests/test_comm.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/tests/test_method.py +0 -0
- {pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/tests/test_sequence.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pychemstation
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.13.dev1
|
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
|
@@ -14,6 +14,7 @@ Requires-Dist: polling
|
|
14
14
|
Requires-Dist: seabreeze
|
15
15
|
Requires-Dist: xsdata
|
16
16
|
Requires-Dist: result
|
17
|
+
Requires-Dist: rainbow
|
17
18
|
|
18
19
|
# Agilent HPLC Macro Control
|
19
20
|
|
@@ -99,7 +100,7 @@ our [GitLab](https://gitlab.com/heingroup/device-api/pychemstation)!
|
|
99
100
|
|
100
101
|
## Authors and Acknowledgements
|
101
102
|
|
102
|
-
Lucy Hao
|
103
|
+
Lucy Hao, Maria Politi
|
103
104
|
|
104
105
|
- Adapted from [**AnalyticalLabware**](https://github.com/croningp/analyticallabware), created by members in the Cronin
|
105
106
|
Group. Copyright © Cronin Group, used under the [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/) license.
|
@@ -82,7 +82,7 @@ our [GitLab](https://gitlab.com/heingroup/device-api/pychemstation)!
|
|
82
82
|
|
83
83
|
## Authors and Acknowledgements
|
84
84
|
|
85
|
-
Lucy Hao
|
85
|
+
Lucy Hao, Maria Politi
|
86
86
|
|
87
87
|
- Adapted from [**AnalyticalLabware**](https://github.com/croningp/analyticallabware), created by members in the Cronin
|
88
88
|
Group. Copyright © Cronin Group, used under the [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/) license.
|
@@ -0,0 +1,9 @@
|
|
1
|
+
"""
|
2
|
+
.. include:: README.md
|
3
|
+
"""
|
4
|
+
|
5
|
+
from .comm import CommunicationController
|
6
|
+
from .devices.pump import PumpController
|
7
|
+
from .devices.column import ColumnController
|
8
|
+
from .tables.method import MethodController
|
9
|
+
from .tables.sequence import SequenceController
|
@@ -0,0 +1,12 @@
|
|
1
|
+
from ....control.controllers import CommunicationController
|
2
|
+
from .device import DeviceController
|
3
|
+
from ....utils.table_types import Table
|
4
|
+
|
5
|
+
|
6
|
+
class ColumnController(DeviceController):
|
7
|
+
|
8
|
+
def __init__(self, controller: CommunicationController, table: Table):
|
9
|
+
super().__init__(controller, table)
|
10
|
+
|
11
|
+
def get_row(self, row: int):
|
12
|
+
pass
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import abc
|
2
|
+
from typing import Union
|
3
|
+
|
4
|
+
from ....control.controllers import CommunicationController
|
5
|
+
from ....control.controllers.tables.table import TableController
|
6
|
+
from ....utils.chromatogram import AgilentChannelChromatogramData
|
7
|
+
from ....utils.table_types import Table
|
8
|
+
|
9
|
+
|
10
|
+
class DeviceController(TableController, abc.ABC):
|
11
|
+
|
12
|
+
def __init__(self, controller: CommunicationController, table: Table):
|
13
|
+
super().__init__(controller, None, None, table)
|
14
|
+
|
15
|
+
@abc.abstractmethod
|
16
|
+
def get_row(self, row: int):
|
17
|
+
pass
|
18
|
+
|
19
|
+
def retrieve_recent_data_files(self):
|
20
|
+
raise NotImplementedError
|
21
|
+
|
22
|
+
def get_data(self) -> Union[list[AgilentChannelChromatogramData], AgilentChannelChromatogramData]:
|
23
|
+
raise NotImplementedError
|
@@ -0,0 +1,43 @@
|
|
1
|
+
from ....control.controllers import CommunicationController
|
2
|
+
from .device import DeviceController
|
3
|
+
from ....utils.pump_types import Pump
|
4
|
+
from ....utils.table_types import Table
|
5
|
+
|
6
|
+
|
7
|
+
class PumpController(DeviceController):
|
8
|
+
|
9
|
+
def __init__(self, controller: CommunicationController, table: Table):
|
10
|
+
super().__init__(controller, table)
|
11
|
+
self.A1 = Pump(in_use=True, solvent="A1")
|
12
|
+
self.B1 = Pump(in_use=True, solvent="B1")
|
13
|
+
self.A2 = Pump(in_use=False, solvent="A2")
|
14
|
+
self.B2 = Pump(in_use=False, solvent="B2")
|
15
|
+
|
16
|
+
def validate_pumps(self):
|
17
|
+
invalid_A_pump_usage = self.A1.in_use and self.A2.in_use
|
18
|
+
invalid_B_pump_usage = self.B1.in_use and self.B2.in_use
|
19
|
+
if invalid_A_pump_usage or invalid_B_pump_usage:
|
20
|
+
raise AttributeError
|
21
|
+
|
22
|
+
def switch_pump(self, num: int, pump: str):
|
23
|
+
if pump == "A":
|
24
|
+
if num == 1:
|
25
|
+
self.A1.in_use = True
|
26
|
+
self.A2.in_use = False
|
27
|
+
elif num == 2:
|
28
|
+
self.A1.in_use = False
|
29
|
+
self.A2.in_use = True
|
30
|
+
elif pump == "B":
|
31
|
+
if num == 1:
|
32
|
+
self.B1.in_use = True
|
33
|
+
self.B2.in_use = False
|
34
|
+
elif num == 2:
|
35
|
+
self.B1.in_use = False
|
36
|
+
self.B2.in_use = True
|
37
|
+
self.purge()
|
38
|
+
|
39
|
+
def purge(self):
|
40
|
+
pass
|
41
|
+
|
42
|
+
def get_row(self, row: int):
|
43
|
+
pass
|
@@ -1,16 +1,15 @@
|
|
1
1
|
import os
|
2
2
|
import time
|
3
|
-
from typing import Optional, Union
|
4
3
|
|
5
4
|
from xsdata.formats.dataclass.parsers import XmlParser
|
6
5
|
|
7
|
-
from
|
8
|
-
from
|
9
|
-
from
|
10
|
-
from
|
11
|
-
from
|
12
|
-
from
|
13
|
-
from
|
6
|
+
from .table import TableController
|
7
|
+
from ....control.controllers import CommunicationController
|
8
|
+
from ....generated import PumpMethod, DadMethod, SolventElement
|
9
|
+
from ....utils.chromatogram import TIME_FORMAT, AgilentChannelChromatogramData
|
10
|
+
from ....utils.macro import *
|
11
|
+
from ....utils.method_types import *
|
12
|
+
from ....utils.table_types import *
|
14
13
|
|
15
14
|
|
16
15
|
class MethodController(TableController):
|
@@ -46,9 +45,21 @@ class MethodController(TableController):
|
|
46
45
|
)
|
47
46
|
|
48
47
|
def get_row(self, row: int) -> TimeTableEntry:
|
48
|
+
flow = None
|
49
|
+
om = None
|
50
|
+
|
51
|
+
try:
|
52
|
+
flow = self.get_num(row, RegisterFlag.TIMETABLE_FLOW)
|
53
|
+
except RuntimeError:
|
54
|
+
pass
|
55
|
+
try:
|
56
|
+
om = self.get_num(row, RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION)
|
57
|
+
except RuntimeError:
|
58
|
+
pass
|
59
|
+
|
49
60
|
return TimeTableEntry(start_time=self.get_num(row, RegisterFlag.TIME),
|
50
|
-
organic_modifer=
|
51
|
-
flow=
|
61
|
+
organic_modifer=om,
|
62
|
+
flow=flow)
|
52
63
|
|
53
64
|
def get_timetable(self, rows: int):
|
54
65
|
uncoalesced_timetable_rows = [self.get_row(r + 1) for r in range(rows)]
|
@@ -84,12 +95,11 @@ class MethodController(TableController):
|
|
84
95
|
cmd=TableOperation.GET_OBJ_HDR_VAL.value.format(
|
85
96
|
register=self.table.register,
|
86
97
|
register_flag=RegisterFlag.POST_TIME))
|
87
|
-
self.table_state = MethodDetails(
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
params=params)
|
98
|
+
self.table_state = MethodDetails(name=method_name,
|
99
|
+
timetable=timetable_rows,
|
100
|
+
stop_time=stop_time,
|
101
|
+
post_time=post_time,
|
102
|
+
params=params)
|
93
103
|
return self.table_state
|
94
104
|
else:
|
95
105
|
raise RuntimeError(rows.err_value)
|
@@ -156,17 +166,16 @@ class MethodController(TableController):
|
|
156
166
|
elif solvent.channel == "Channel_B":
|
157
167
|
organic_modifier = solvent
|
158
168
|
|
159
|
-
self.table_state = MethodDetails(
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
dad_wavelengthes=dad.signals.signal)
|
169
|
+
self.table_state = MethodDetails(name=method_name,
|
170
|
+
params=HPLCMethodParams(organic_modifier=organic_modifier.percentage,
|
171
|
+
flow=method.flow),
|
172
|
+
stop_time=method.stop_time.stop_time_value,
|
173
|
+
post_time=method.post_time.post_time_value,
|
174
|
+
timetable=[TimeTableEntry(start_time=tte.time,
|
175
|
+
organic_modifer=tte.percent_b,
|
176
|
+
flow=method.flow
|
177
|
+
) for tte in method.timetable.timetable_entry],
|
178
|
+
dad_wavelengthes=dad.signals.signal)
|
170
179
|
return self.table_state
|
171
180
|
else:
|
172
181
|
raise FileNotFoundError
|
@@ -186,7 +195,7 @@ class MethodController(TableController):
|
|
186
195
|
ptype=PType.NUM)
|
187
196
|
post_time: Param = Param(val=updated_method.post_time,
|
188
197
|
chemstation_key=RegisterFlag.POST_TIME,
|
189
|
-
ptype=PType.NUM)
|
198
|
+
ptype=PType.NUM)
|
190
199
|
flow: Param = Param(val=updated_method.params.flow,
|
191
200
|
chemstation_key=RegisterFlag.FLOW,
|
192
201
|
ptype=PType.NUM)
|
@@ -205,19 +214,15 @@ class MethodController(TableController):
|
|
205
214
|
self._update_param(initial_organic_modifier)
|
206
215
|
self._update_param(flow)
|
207
216
|
if self.table_state.stop_time:
|
208
|
-
self._update_param(Param(val="Set",
|
209
|
-
chemstation_key=RegisterFlag.STOPTIME_MODE,
|
210
|
-
ptype=PType.STR))
|
217
|
+
self._update_param(Param(val="Set", chemstation_key=RegisterFlag.STOPTIME_MODE, ptype=PType.STR))
|
211
218
|
self._update_param(max_time)
|
212
219
|
else:
|
213
|
-
self._update_param(Param(val="Off",
|
214
|
-
chemstation_key=RegisterFlag.STOPTIME_MODE,
|
215
|
-
ptype=PType.STR))
|
220
|
+
self._update_param(Param(val="Off", chemstation_key=RegisterFlag.STOPTIME_MODE, ptype=PType.STR))
|
216
221
|
if self.table_state.post_time:
|
217
|
-
self._update_param(Param(val="Set",
|
218
|
-
chemstation_key=RegisterFlag.POSTIME_MODE,
|
219
|
-
ptype=PType.STR))
|
222
|
+
self._update_param(Param(val="Set", chemstation_key=RegisterFlag.POSTIME_MODE, ptype=PType.STR))
|
220
223
|
self._update_param(post_time)
|
224
|
+
else:
|
225
|
+
self._update_param(Param(val="Off", chemstation_key=RegisterFlag.POSTIME_MODE, ptype=PType.STR))
|
221
226
|
self.download()
|
222
227
|
|
223
228
|
def _update_param(self, method_param: Param):
|
@@ -246,34 +251,33 @@ class MethodController(TableController):
|
|
246
251
|
|
247
252
|
def edit_row(self, row: TimeTableEntry, first_row: bool = False):
|
248
253
|
if first_row:
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
254
|
+
if row.organic_modifer:
|
255
|
+
self.add_row()
|
256
|
+
self.add_new_col_text(col_name=RegisterFlag.FUNCTION, val=RegisterFlag.SOLVENT_COMPOSITION.value)
|
257
|
+
self.add_new_col_num(col_name=RegisterFlag.TIME, val=row.start_time)
|
258
|
+
self.add_new_col_num(col_name=RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION, val=row.organic_modifer)
|
259
|
+
if row.flow:
|
260
|
+
self.add_row()
|
261
|
+
self.get_num_rows()
|
262
|
+
self.edit_row_text(col_name=RegisterFlag.FUNCTION, val=RegisterFlag.FLOW.value)
|
263
|
+
self.add_new_col_num(col_name=RegisterFlag.TIMETABLE_FLOW, val=row.flow)
|
264
|
+
self.edit_row_num(col_name=RegisterFlag.TIMETABLE_FLOW, val=row.flow)
|
260
265
|
self.download()
|
261
266
|
else:
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
self.download()
|
267
|
+
if row.organic_modifer:
|
268
|
+
self.add_row()
|
269
|
+
self.get_num_rows()
|
270
|
+
self.edit_row_text(col_name=RegisterFlag.FUNCTION, val=RegisterFlag.SOLVENT_COMPOSITION.value)
|
271
|
+
self.edit_row_num(col_name=RegisterFlag.TIME, val=row.start_time)
|
272
|
+
self.edit_row_num(col_name=RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION, val=row.organic_modifer)
|
273
|
+
self.download()
|
274
|
+
if row.flow:
|
275
|
+
self.add_row()
|
276
|
+
self.get_num_rows()
|
277
|
+
self.edit_row_text(col_name=RegisterFlag.FUNCTION, val=RegisterFlag.FLOW.value)
|
278
|
+
self.edit_row_num(col_name=RegisterFlag.TIMETABLE_FLOW, val=row.flow)
|
279
|
+
self.edit_row_num(col_name=RegisterFlag.TIME, val=row.start_time)
|
280
|
+
self.download()
|
277
281
|
|
278
282
|
def _update_method_timetable(self, timetable_rows: list[TimeTableEntry]):
|
279
283
|
self.get_num_rows()
|
@@ -309,30 +313,34 @@ class MethodController(TableController):
|
|
309
313
|
if not self.table_state:
|
310
314
|
self.table_state = self.load()
|
311
315
|
|
312
|
-
|
313
|
-
self.
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
316
|
+
folder_name = ""
|
317
|
+
hplc_is_running = self.check_hplc_is_running()
|
318
|
+
while not hplc_is_running:
|
319
|
+
timestamp = time.strftime(TIME_FORMAT)
|
320
|
+
self.send(Command.RUN_METHOD_CMD.value.format(data_dir=self.data_dir,
|
321
|
+
experiment_name=experiment_name,
|
322
|
+
timestamp=timestamp))
|
318
323
|
folder_name = f"{experiment_name}_{timestamp}.D"
|
319
|
-
self.
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
324
|
+
hplc_is_running = self.check_hplc_is_running()
|
325
|
+
|
326
|
+
self.data_files.append(os.path.join(self.data_dir, folder_name))
|
327
|
+
|
328
|
+
if stall_while_running:
|
329
|
+
run_completed = self.check_hplc_done_running(method=self.table_state)
|
330
|
+
if run_completed.is_ok():
|
331
|
+
self.data_files[-1] = run_completed.ok_value
|
327
332
|
else:
|
328
|
-
|
333
|
+
raise RuntimeError("Run error has occurred.")
|
334
|
+
else:
|
335
|
+
self.data_files[-1].dir = self.fuzzy_match_most_recent_folder(folder_name).ok_value
|
329
336
|
|
330
337
|
def retrieve_recent_data_files(self) -> str:
|
331
338
|
return self.data_files[-1]
|
332
339
|
|
333
|
-
def get_data(self, custom_path: Optional[str] = None
|
340
|
+
def get_data(self, custom_path: Optional[str] = None,
|
341
|
+
read_uv: bool = False) -> AgilentChannelChromatogramData:
|
334
342
|
if not custom_path:
|
335
|
-
self.get_spectrum(self.data_files[-1])
|
343
|
+
self.get_spectrum(self.data_files[-1], read_uv)
|
336
344
|
else:
|
337
|
-
self.get_spectrum(custom_path)
|
338
|
-
return AgilentChannelChromatogramData(**self.spectra)
|
345
|
+
self.get_spectrum(custom_path, read_uv)
|
346
|
+
return AgilentChannelChromatogramData(**self.spectra) if not read_uv else self.uv
|
@@ -1,14 +1,14 @@
|
|
1
1
|
import os
|
2
2
|
import time
|
3
|
-
from typing import Optional
|
3
|
+
from typing import Optional, Union
|
4
4
|
|
5
|
-
from
|
6
|
-
from
|
7
|
-
from
|
8
|
-
from
|
9
|
-
from
|
10
|
-
from
|
11
|
-
from
|
5
|
+
from .table import TableController, ChromData
|
6
|
+
from ....control.controllers.comm import CommunicationController
|
7
|
+
from ....utils.chromatogram import SEQUENCE_TIME_FORMAT, AgilentChannelChromatogramData
|
8
|
+
from ....utils.macro import Command
|
9
|
+
from ....utils.sequence_types import SequenceTable, SequenceEntry, SequenceDataFiles, InjectionSource, SampleType
|
10
|
+
from ....utils.table_types import RegisterFlag, Table
|
11
|
+
from ....utils.tray_types import TenVialColumn, FiftyFourVialPlate
|
12
12
|
|
13
13
|
|
14
14
|
class SequenceController(TableController):
|
@@ -117,8 +117,10 @@ class SequenceController(TableController):
|
|
117
117
|
|
118
118
|
if row.vial_location:
|
119
119
|
loc = row.vial_location
|
120
|
-
if isinstance(
|
120
|
+
if isinstance(loc, TenVialColumn):
|
121
121
|
loc = row.vial_location.value
|
122
|
+
elif isinstance(loc, FiftyFourVialPlate):
|
123
|
+
loc = row.vial_location.value()
|
122
124
|
self.edit_row_num(row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc)
|
123
125
|
|
124
126
|
if row.method:
|
@@ -139,7 +141,10 @@ class SequenceController(TableController):
|
|
139
141
|
|
140
142
|
if row.sample_name:
|
141
143
|
self.edit_row_text(row=row_num, col_name=RegisterFlag.NAME, val=row.sample_name)
|
142
|
-
|
144
|
+
if row.data_file:
|
145
|
+
self.edit_row_text(row=row_num, col_name=RegisterFlag.DATA_FILE, val=row.data_file)
|
146
|
+
else:
|
147
|
+
self.edit_row_text(row=row_num, col_name=RegisterFlag.DATA_FILE, val=row.sample_name)
|
143
148
|
|
144
149
|
if row.sample_type:
|
145
150
|
self.edit_row_num(row=row_num, col_name=RegisterFlag.SAMPLE_TYPE, val=row.sample_type.value)
|
@@ -176,15 +181,19 @@ class SequenceController(TableController):
|
|
176
181
|
sequence_data_files: SequenceDataFiles = self.data_files[-1]
|
177
182
|
return sequence_data_files.dir
|
178
183
|
|
179
|
-
def get_data(self, custom_path: Optional[str] = None
|
184
|
+
def get_data(self, custom_path: Optional[str] = None,
|
185
|
+
read_uv: bool = False) -> list[AgilentChannelChromatogramData]:
|
180
186
|
parent_dir = self.data_files[-1].dir if not custom_path else custom_path
|
181
187
|
subdirs = [x[0] for x in os.walk(self.data_dir)]
|
182
188
|
potential_folders = sorted(list(filter(lambda d: parent_dir in d, subdirs)))
|
183
189
|
self.data_files[-1].child_dirs = [f for f in potential_folders if
|
184
190
|
parent_dir in f and ".M" not in f and ".D" in f]
|
185
191
|
|
186
|
-
spectra: list[AgilentChannelChromatogramData] = []
|
192
|
+
spectra: list[Union[AgilentChannelChromatogramData, ChromData]] = []
|
193
|
+
all_w_spectra: list[Union[AgilentChannelChromatogramData, ChromData]] = []
|
187
194
|
for row in self.data_files[-1].child_dirs:
|
188
|
-
self.get_spectrum(row)
|
195
|
+
self.get_spectrum(row, read_uv)
|
189
196
|
spectra.append(AgilentChannelChromatogramData(**self.spectra))
|
190
|
-
|
197
|
+
all_w_spectra.append(self.uv)
|
198
|
+
|
199
|
+
return spectra if not read_uv else all_w_spectra
|
@@ -6,21 +6,32 @@ Authors: Lucy Hao
|
|
6
6
|
|
7
7
|
import abc
|
8
8
|
import os
|
9
|
+
from dataclasses import dataclass
|
9
10
|
from typing import Union, Optional
|
10
11
|
|
12
|
+
import numpy as np
|
11
13
|
import polling
|
14
|
+
from rainbow import DataFile
|
12
15
|
from result import Result, Ok, Err
|
16
|
+
import pandas as pd
|
17
|
+
import rainbow as rb
|
13
18
|
|
14
|
-
from
|
15
|
-
from
|
16
|
-
from
|
17
|
-
from
|
18
|
-
from
|
19
|
-
from
|
19
|
+
from ....control.controllers.comm import CommunicationController
|
20
|
+
from ....utils.chromatogram import AgilentHPLCChromatogram, AgilentChannelChromatogramData
|
21
|
+
from ....utils.macro import Command, HPLCRunningStatus, Response
|
22
|
+
from ....utils.method_types import MethodDetails
|
23
|
+
from ....utils.sequence_types import SequenceDataFiles, SequenceTable
|
24
|
+
from ....utils.table_types import Table, TableOperation, RegisterFlag
|
20
25
|
|
21
26
|
TableType = Union[MethodDetails, SequenceTable]
|
22
27
|
|
23
28
|
|
29
|
+
@dataclass
|
30
|
+
class ChromData:
|
31
|
+
x: np.array
|
32
|
+
y: np.array
|
33
|
+
|
34
|
+
|
24
35
|
class TableController(abc.ABC):
|
25
36
|
|
26
37
|
def __init__(self, controller: CommunicationController, src: str, data_dir: str, table: Table):
|
@@ -51,6 +62,8 @@ class TableController(abc.ABC):
|
|
51
62
|
|
52
63
|
self.data_files: Union[list[SequenceDataFiles], list[str]] = []
|
53
64
|
|
65
|
+
self.uv = None
|
66
|
+
|
54
67
|
# Initialize row counter for table operations
|
55
68
|
self.send('Local Rows')
|
56
69
|
|
@@ -191,8 +204,8 @@ class TableController(abc.ABC):
|
|
191
204
|
def check_hplc_is_running(self) -> bool:
|
192
205
|
started_running = polling.poll(
|
193
206
|
lambda: isinstance(self.controller.get_status(), HPLCRunningStatus),
|
194
|
-
step=
|
195
|
-
max_tries=
|
207
|
+
step=2,
|
208
|
+
max_tries=10)
|
196
209
|
return started_running
|
197
210
|
|
198
211
|
def check_hplc_done_running(self,
|
@@ -254,13 +267,21 @@ class TableController(abc.ABC):
|
|
254
267
|
def get_data(self) -> Union[list[AgilentChannelChromatogramData], AgilentChannelChromatogramData]:
|
255
268
|
pass
|
256
269
|
|
257
|
-
def
|
270
|
+
def get_uv_spectrum(self, path: str):
|
271
|
+
data_uv: DataFile = rb.agilent.chemstation.parse_file(os.path.join(path, "DAD1.UV"))
|
272
|
+
zipped_data = zip(data_uv.ylabels, data_uv.data)
|
273
|
+
self.uv = {str(w_a[0]): ChromData(x=data_uv.xlabels, y=w_a[1]) for w_a in zipped_data}
|
274
|
+
|
275
|
+
def get_spectrum(self, data_path: str, read_uv: bool = False):
|
258
276
|
"""
|
259
277
|
Load chromatogram for any channel in spectra dictionary.
|
260
278
|
"""
|
279
|
+
if read_uv:
|
280
|
+
self.get_uv_spectrum(data_path)
|
281
|
+
|
261
282
|
for channel, spec in self.spectra.items():
|
262
283
|
try:
|
263
|
-
spec.load_spectrum(data_path=
|
284
|
+
spec.load_spectrum(data_path=data_path, channel=channel)
|
264
285
|
except FileNotFoundError:
|
265
286
|
self.spectra[channel] = None
|
266
287
|
print(f"No data at channel: {channel}")
|
File without changes
|
@@ -0,0 +1,30 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
from pychemstation.utils.tray_types import Tray
|
5
|
+
|
6
|
+
|
7
|
+
@dataclass
|
8
|
+
class Draw:
|
9
|
+
amount: float
|
10
|
+
source: Tray
|
11
|
+
speed: Any
|
12
|
+
offset: Any
|
13
|
+
|
14
|
+
|
15
|
+
@dataclass
|
16
|
+
class Wait:
|
17
|
+
time: int
|
18
|
+
|
19
|
+
|
20
|
+
@dataclass
|
21
|
+
class Inject:
|
22
|
+
pass
|
23
|
+
|
24
|
+
|
25
|
+
InjectorFunction = [Draw, Wait, Inject]
|
26
|
+
|
27
|
+
|
28
|
+
@dataclass
|
29
|
+
class InjectorTable:
|
30
|
+
functions: list[InjectorFunction]
|
@@ -34,6 +34,7 @@ class Command(Enum):
|
|
34
34
|
INSTRUMENT_OFF = 'macro "SHUTDOWN.MAC" ,go'
|
35
35
|
INSTRUMENT_ON = 'LIDoOperation "TURN_ON"'
|
36
36
|
|
37
|
+
# Method and Sequence Related
|
37
38
|
GET_METHOD_CMD = "response$ = _MethFile$"
|
38
39
|
GET_ROWS_CMD = 'response_num = TabHdrVal({register}, "{table_name}", "{col_name}")'
|
39
40
|
SWITCH_METHOD_CMD = 'LoadMethod "{method_dir}", "{method_name}.M"'
|
@@ -4,7 +4,7 @@ from dataclasses import dataclass
|
|
4
4
|
from enum import Enum
|
5
5
|
from typing import Optional, Union
|
6
6
|
|
7
|
-
from pychemstation.utils.tray_types import TenVialColumn
|
7
|
+
from pychemstation.utils.tray_types import TenVialColumn, Tray
|
8
8
|
|
9
9
|
|
10
10
|
@dataclass
|
@@ -39,7 +39,8 @@ class InjectionSource(Enum):
|
|
39
39
|
@dataclass
|
40
40
|
class SequenceEntry:
|
41
41
|
sample_name: str
|
42
|
-
vial_location:
|
42
|
+
vial_location: Tray
|
43
|
+
data_file: Optional[str] = None
|
43
44
|
method: Optional[str] = None
|
44
45
|
num_inj: Optional[int] = 1
|
45
46
|
inj_vol: Optional[int] = 2
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from enum import Enum
|
5
|
+
from typing import Union
|
5
6
|
|
6
7
|
|
7
8
|
class Num(Enum):
|
@@ -50,3 +51,6 @@ class TenVialColumn(Enum):
|
|
50
51
|
EIGHT = 8
|
51
52
|
NINE = 9
|
52
53
|
TEN = 10
|
54
|
+
|
55
|
+
|
56
|
+
Tray = Union[FiftyFourVialPlate, TenVialColumn]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pychemstation
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.13.dev1
|
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
|
@@ -14,6 +14,7 @@ Requires-Dist: polling
|
|
14
14
|
Requires-Dist: seabreeze
|
15
15
|
Requires-Dist: xsdata
|
16
16
|
Requires-Dist: result
|
17
|
+
Requires-Dist: rainbow
|
17
18
|
|
18
19
|
# Agilent HPLC Macro Control
|
19
20
|
|
@@ -99,7 +100,7 @@ our [GitLab](https://gitlab.com/heingroup/device-api/pychemstation)!
|
|
99
100
|
|
100
101
|
## Authors and Acknowledgements
|
101
102
|
|
102
|
-
Lucy Hao
|
103
|
+
Lucy Hao, Maria Politi
|
103
104
|
|
104
105
|
- Adapted from [**AnalyticalLabware**](https://github.com/croningp/analyticallabware), created by members in the Cronin
|
105
106
|
Group. Copyright © Cronin Group, used under the [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/) license.
|
@@ -16,17 +16,24 @@ pychemstation/control/__init__.py
|
|
16
16
|
pychemstation/control/hplc.py
|
17
17
|
pychemstation/control/controllers/__init__.py
|
18
18
|
pychemstation/control/controllers/comm.py
|
19
|
-
pychemstation/control/controllers/
|
20
|
-
pychemstation/control/controllers/
|
21
|
-
pychemstation/control/controllers/
|
19
|
+
pychemstation/control/controllers/devices/__init__.py
|
20
|
+
pychemstation/control/controllers/devices/column.py
|
21
|
+
pychemstation/control/controllers/devices/device.py
|
22
|
+
pychemstation/control/controllers/devices/pump.py
|
23
|
+
pychemstation/control/controllers/tables/__init__.py
|
24
|
+
pychemstation/control/controllers/tables/method.py
|
25
|
+
pychemstation/control/controllers/tables/sequence.py
|
26
|
+
pychemstation/control/controllers/tables/table.py
|
22
27
|
pychemstation/generated/__init__.py
|
23
28
|
pychemstation/generated/dad_method.py
|
24
29
|
pychemstation/generated/pump_method.py
|
25
30
|
pychemstation/utils/__init__.py
|
26
31
|
pychemstation/utils/chromatogram.py
|
32
|
+
pychemstation/utils/injector_types.py
|
27
33
|
pychemstation/utils/macro.py
|
28
34
|
pychemstation/utils/method_types.py
|
29
35
|
pychemstation/utils/parsing.py
|
36
|
+
pychemstation/utils/pump_types.py
|
30
37
|
pychemstation/utils/sequence_types.py
|
31
38
|
pychemstation/utils/table_types.py
|
32
39
|
pychemstation/utils/tray_types.py
|
@@ -34,5 +41,6 @@ tests/__init__.py
|
|
34
41
|
tests/constants.py
|
35
42
|
tests/test_comb.py
|
36
43
|
tests/test_comm.py
|
44
|
+
tests/test_inj.py
|
37
45
|
tests/test_method.py
|
38
46
|
tests/test_sequence.py
|
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
|
|
5
5
|
|
6
6
|
setuptools.setup(
|
7
7
|
name="pychemstation",
|
8
|
-
version="0.5.
|
8
|
+
version="0.5.13.dev1",
|
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",
|
@@ -17,7 +17,8 @@ setuptools.setup(
|
|
17
17
|
'polling',
|
18
18
|
'seabreeze',
|
19
19
|
'xsdata',
|
20
|
-
'result'
|
20
|
+
'result',
|
21
|
+
'rainbow'
|
21
22
|
],
|
22
23
|
classifiers=[
|
23
24
|
"Programming Language :: Python :: 3",
|
File without changes
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import os
|
2
|
+
import unittest
|
3
|
+
|
4
|
+
from pychemstation.control import HPLCController
|
5
|
+
from tests.constants import *
|
6
|
+
|
7
|
+
|
8
|
+
class TestInj(unittest.TestCase):
|
9
|
+
def setUp(self):
|
10
|
+
path_constants = room(254)
|
11
|
+
for path in path_constants:
|
12
|
+
if not os.path.exists(path):
|
13
|
+
self.fail(
|
14
|
+
f"{path} does not exist on your system. If you would like to run tests, please change this path.")
|
15
|
+
|
16
|
+
self.hplc_controller = HPLCController(comm_dir=path_constants[0],
|
17
|
+
method_dir=path_constants[1],
|
18
|
+
data_dir=path_constants[2],
|
19
|
+
sequence_dir=path_constants[3])
|
20
|
+
|
21
|
+
def test_load_inj(self):
|
22
|
+
self.hplc_controller.switch_method(DEFAULT_METHOD)
|
23
|
+
try:
|
24
|
+
gp_mtd = self.hplc_controller.method_controller.load_from_disk(DEFAULT_METHOD)
|
25
|
+
self.assertTrue(gp_mtd.first_row.organic_modifier == 5)
|
26
|
+
except Exception as e:
|
27
|
+
self.fail(f"Should have not failed, {e}")
|
28
|
+
|
29
|
+
def test_edit_inj(self):
|
30
|
+
self.hplc_controller.method_controller.switch(DEFAULT_METHOD)
|
31
|
+
new_method = gen_rand_method()
|
32
|
+
try:
|
33
|
+
self.hplc_controller.edit_method(new_method)
|
34
|
+
except Exception as e:
|
35
|
+
self.fail(f"Should have not failed: {e}")
|
36
|
+
|
37
|
+
if __name__ == '__main__':
|
38
|
+
unittest.main()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/pychemstation/control/controllers/comm.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
|
{pychemstation-0.5.12 → pychemstation-0.5.13.dev1}/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
|
File without changes
|
File without changes
|