pychemstation 0.8.3__py3-none-any.whl → 0.10.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pychemstation/analysis/__init__.py +4 -0
- pychemstation/analysis/base_spectrum.py +9 -9
- pychemstation/analysis/process_report.py +13 -7
- pychemstation/analysis/utils.py +1 -3
- pychemstation/control/__init__.py +4 -0
- pychemstation/control/comm.py +206 -0
- pychemstation/control/controllers/__init__.py +6 -0
- pychemstation/control/controllers/comm.py +12 -5
- pychemstation/control/controllers/devices/column.py +12 -0
- pychemstation/control/controllers/devices/dad.py +0 -0
- pychemstation/control/controllers/devices/device.py +10 -7
- pychemstation/control/controllers/devices/injector.py +18 -84
- pychemstation/control/controllers/devices/pump.py +43 -0
- pychemstation/control/controllers/method.py +338 -0
- pychemstation/control/controllers/sequence.py +190 -0
- pychemstation/control/controllers/table_controller.py +266 -0
- pychemstation/control/controllers/tables/method.py +35 -13
- pychemstation/control/controllers/tables/sequence.py +46 -37
- pychemstation/control/controllers/tables/table.py +46 -30
- pychemstation/control/hplc.py +27 -11
- pychemstation/control/table/__init__.py +3 -0
- pychemstation/control/table/method.py +274 -0
- pychemstation/control/table/sequence.py +210 -0
- pychemstation/control/table/table_controller.py +201 -0
- pychemstation/generated/dad_method.py +1 -1
- pychemstation/generated/pump_method.py +1 -1
- pychemstation/utils/chromatogram.py +2 -5
- pychemstation/utils/injector_types.py +1 -1
- pychemstation/utils/macro.py +3 -3
- pychemstation/utils/method_types.py +2 -2
- pychemstation/utils/num_utils.py +65 -0
- pychemstation/utils/parsing.py +1 -0
- pychemstation/utils/sequence_types.py +3 -3
- pychemstation/utils/spec_utils.py +304 -0
- {pychemstation-0.8.3.dist-info → pychemstation-0.10.0.dist-info}/METADATA +19 -8
- pychemstation-0.10.0.dist-info/RECORD +62 -0
- {pychemstation-0.8.3.dist-info → pychemstation-0.10.0.dist-info}/WHEEL +2 -1
- pychemstation-0.10.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/constants.py +134 -0
- tests/test_comb.py +136 -0
- tests/test_comm.py +65 -0
- tests/test_inj.py +39 -0
- tests/test_method.py +99 -0
- tests/test_nightly.py +80 -0
- tests/test_offline_stable.py +69 -0
- tests/test_online_stable.py +275 -0
- tests/test_proc_rep.py +52 -0
- tests/test_runs_stable.py +225 -0
- tests/test_sequence.py +125 -0
- tests/test_stable.py +276 -0
- pychemstation/control/README.md +0 -124
- pychemstation/control/controllers/README.md +0 -1
- pychemstation-0.8.3.dist-info/RECORD +0 -37
- {pychemstation-0.8.3.dist-info → pychemstation-0.10.0.dist-info}/licenses/LICENSE +0 -0
@@ -3,24 +3,34 @@ Abstract module containing shared logic for Method and Sequence tables.
|
|
3
3
|
|
4
4
|
Authors: Lucy Hao
|
5
5
|
"""
|
6
|
+
from __future__ import annotations
|
6
7
|
|
7
8
|
import abc
|
8
9
|
import math
|
9
10
|
import os
|
10
11
|
import time
|
11
|
-
|
12
|
+
import warnings
|
13
|
+
from typing import Dict, List, Optional, Tuple, Union
|
12
14
|
|
13
15
|
import polling
|
14
16
|
import rainbow as rb
|
15
|
-
from result import Result,
|
16
|
-
|
17
|
-
from ....analysis.process_report import
|
17
|
+
from result import Err, Result, Ok
|
18
|
+
|
19
|
+
from ....analysis.process_report import (
|
20
|
+
AgilentReport,
|
21
|
+
CSVProcessor,
|
22
|
+
ReportType,
|
23
|
+
TXTProcessor,
|
24
|
+
)
|
18
25
|
from ....control.controllers.comm import CommunicationController
|
19
|
-
from ....utils.chromatogram import
|
26
|
+
from ....utils.chromatogram import (
|
27
|
+
AgilentChannelChromatogramData,
|
28
|
+
AgilentHPLCChromatogram,
|
29
|
+
)
|
20
30
|
from ....utils.macro import Command, HPLCRunningStatus, Response
|
21
31
|
from ....utils.method_types import MethodDetails
|
22
32
|
from ....utils.sequence_types import SequenceDataFiles, SequenceTable
|
23
|
-
from ....utils.table_types import
|
33
|
+
from ....utils.table_types import RegisterFlag, T, Table, TableOperation
|
24
34
|
|
25
35
|
TableType = Union[MethodDetails, SequenceTable]
|
26
36
|
|
@@ -200,21 +210,25 @@ class TableController(abc.ABC):
|
|
200
210
|
return started_running
|
201
211
|
|
202
212
|
def check_hplc_run_finished(self) -> Tuple[float, bool]:
|
203
|
-
|
204
|
-
if
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
213
|
+
done_running = self.controller.check_if_not_running()
|
214
|
+
if self.curr_run_starting_time and self.timeout:
|
215
|
+
time_passed = (time.time() - self.curr_run_starting_time)
|
216
|
+
if time_passed > self.timeout:
|
217
|
+
enough_time_passed = time_passed >= self.timeout
|
218
|
+
run_finished = enough_time_passed and done_running
|
219
|
+
if run_finished:
|
220
|
+
self._reset_time()
|
221
|
+
return 0, run_finished
|
222
|
+
else:
|
223
|
+
time_left = self.timeout - time_passed
|
224
|
+
return time_left, self.controller.check_if_not_running()
|
225
|
+
return 0, self.controller.check_if_not_running()
|
226
|
+
|
227
|
+
def check_hplc_done_running(self) -> Ok[T] | Err[str]:
|
214
228
|
"""
|
215
229
|
Checks if ChemStation has finished running and can read data back
|
216
230
|
|
217
|
-
:return:
|
231
|
+
:return: Data file object containing most recent run file information.
|
218
232
|
"""
|
219
233
|
finished_run = False
|
220
234
|
minutes = math.ceil(self.timeout / 60)
|
@@ -244,16 +258,18 @@ class TableController(abc.ABC):
|
|
244
258
|
except Exception:
|
245
259
|
self._reset_time()
|
246
260
|
return check_folder
|
247
|
-
|
248
|
-
else:
|
249
|
-
return Err("Run did not complete as expected")
|
261
|
+
return Err("Run did not complete as expected")
|
250
262
|
|
251
263
|
@abc.abstractmethod
|
252
264
|
def fuzzy_match_most_recent_folder(self, most_recent_folder: T) -> Result[T, str]:
|
253
265
|
pass
|
254
266
|
|
255
267
|
@abc.abstractmethod
|
256
|
-
def get_data(self) -> Union[List[AgilentChannelChromatogramData], AgilentChannelChromatogramData]:
|
268
|
+
def get_data(self, custom_path: Optional[str] = None) -> Union[List[AgilentChannelChromatogramData], AgilentChannelChromatogramData]:
|
269
|
+
pass
|
270
|
+
|
271
|
+
@abc.abstractmethod
|
272
|
+
def get_data_uv(self) -> Union[List[Dict[str, AgilentHPLCChromatogram]], Dict[str, AgilentHPLCChromatogram]]:
|
257
273
|
pass
|
258
274
|
|
259
275
|
@abc.abstractmethod
|
@@ -263,11 +279,11 @@ class TableController(abc.ABC):
|
|
263
279
|
def get_uv_spectrum(self, path: str):
|
264
280
|
data_uv = rb.agilent.chemstation.parse_file(os.path.join(path, "DAD1.UV"))
|
265
281
|
times = data_uv.xlabels
|
266
|
-
|
267
|
-
|
268
|
-
for (i, w) in enumerate(
|
282
|
+
wavelengths = data_uv.ylabels
|
283
|
+
absorbances = data_uv.data.transpose()
|
284
|
+
for (i, w) in enumerate(wavelengths):
|
269
285
|
self.uv[w] = AgilentHPLCChromatogram()
|
270
|
-
self.uv[w].attach_spectrum(times,
|
286
|
+
self.uv[w].attach_spectrum(times, absorbances[i])
|
271
287
|
|
272
288
|
def get_report_details(self, path: str,
|
273
289
|
report_type: ReportType = ReportType.TXT) -> AgilentReport:
|
@@ -281,18 +297,18 @@ class TableController(abc.ABC):
|
|
281
297
|
self.report = csv_report.ok_value
|
282
298
|
return self.report
|
283
299
|
|
284
|
-
|
300
|
+
|
301
|
+
def get_spectrum_at_channels(self, data_path: str):
|
285
302
|
"""
|
286
303
|
Load chromatogram for any channel in spectra dictionary.
|
287
304
|
"""
|
288
|
-
if read_uv:
|
289
|
-
self.get_uv_spectrum(data_path)
|
290
305
|
for channel, spec in self.spectra.items():
|
291
306
|
try:
|
292
307
|
spec.load_spectrum(data_path=data_path, channel=channel)
|
293
308
|
except FileNotFoundError:
|
294
309
|
self.spectra[channel] = AgilentHPLCChromatogram()
|
295
|
-
|
310
|
+
warning = f"No data at channel: {channel}"
|
311
|
+
warnings.warn(warning)
|
296
312
|
|
297
313
|
def _reset_time(self):
|
298
314
|
self.curr_run_starting_time = None
|
pychemstation/control/hplc.py
CHANGED
@@ -3,12 +3,20 @@ Module to provide API for higher-level HPLC actions.
|
|
3
3
|
|
4
4
|
Authors: Lucy Hao
|
5
5
|
"""
|
6
|
+
from __future__ import annotations
|
6
7
|
|
7
|
-
from typing import
|
8
|
+
from typing import Dict, List, Optional, Tuple, Union
|
8
9
|
|
10
|
+
from pychemstation.utils.chromatogram import (
|
11
|
+
AgilentHPLCChromatogram,
|
12
|
+
)
|
9
13
|
from .controllers.devices.injector import InjectorController
|
10
|
-
from ..analysis.process_report import
|
11
|
-
from ..control.controllers import
|
14
|
+
from ..analysis.process_report import AgilentReport, ReportType
|
15
|
+
from ..control.controllers import (
|
16
|
+
CommunicationController,
|
17
|
+
MethodController,
|
18
|
+
SequenceController,
|
19
|
+
)
|
12
20
|
from ..utils.chromatogram import AgilentChannelChromatogramData
|
13
21
|
from ..utils.injector_types import InjectorTable
|
14
22
|
from ..utils.macro import Command, Response, Status
|
@@ -44,7 +52,8 @@ class HPLCController:
|
|
44
52
|
method_dir: str,
|
45
53
|
sequence_dir: str,
|
46
54
|
data_dirs: List[str],
|
47
|
-
offline: bool = False
|
55
|
+
offline: bool = False,
|
56
|
+
debug: bool = False,):
|
48
57
|
"""Initialize HPLC controller. The `hplc_talk.mac` macro file must be loaded in the Chemstation software.
|
49
58
|
`comm_dir` must match the file path in the macro file. All file paths are normal strings, with the left slash
|
50
59
|
double escaped: "C:\\my_folder\\"
|
@@ -56,7 +65,7 @@ class HPLCController:
|
|
56
65
|
:param sequence_dir: Name of directory where sequence files are stored.
|
57
66
|
:raises FileNotFoundError: If either `data_dir`, `method_dir`, `sequence_dir`, `sequence_data_dir`or `comm_dir` is not a valid directory.
|
58
67
|
"""
|
59
|
-
self.comm = CommunicationController(comm_dir=comm_dir) if not offline else None
|
68
|
+
self.comm = CommunicationController(comm_dir=comm_dir, debug=debug) if not offline else None
|
60
69
|
self.method_controller = MethodController(controller=self.comm,
|
61
70
|
src=method_dir,
|
62
71
|
data_dirs=data_dirs,
|
@@ -200,14 +209,17 @@ class HPLCController:
|
|
200
209
|
return self.method_controller.get_report(custom_path=custom_path, report_type=report_type)[0]
|
201
210
|
|
202
211
|
def get_last_run_method_data(self, read_uv: bool = False,
|
203
|
-
|
212
|
+
custom_path: Optional[str] = None) -> Dict[str, AgilentHPLCChromatogram] | AgilentChannelChromatogramData:
|
204
213
|
"""
|
205
214
|
Returns the last run method data.
|
206
215
|
|
207
|
-
:param
|
216
|
+
: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.
|
208
217
|
:param read_uv: whether to also read the UV file
|
209
218
|
"""
|
210
|
-
|
219
|
+
if read_uv:
|
220
|
+
return self.method_controller.get_data_uv(custom_path=custom_path)
|
221
|
+
else:
|
222
|
+
return self.method_controller.get_data(custom_path=custom_path)
|
211
223
|
|
212
224
|
def get_last_run_sequence_reports(self,
|
213
225
|
custom_path: Optional[str] = None,
|
@@ -221,14 +233,18 @@ class HPLCController:
|
|
221
233
|
return self.sequence_controller.get_report(custom_path=custom_path, report_type=report_type)
|
222
234
|
|
223
235
|
def get_last_run_sequence_data(self, read_uv: bool = False,
|
224
|
-
|
236
|
+
custom_path: Optional[str] = None) -> List[Dict[str, AgilentHPLCChromatogram]] | \
|
237
|
+
List[AgilentChannelChromatogramData]:
|
225
238
|
"""
|
226
239
|
Returns data for all rows in the last run sequence data.
|
227
240
|
|
228
|
-
:param
|
241
|
+
: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.
|
229
242
|
:param read_uv: whether to also read the UV file
|
230
243
|
"""
|
231
|
-
|
244
|
+
if read_uv:
|
245
|
+
return self.sequence_controller.get_data_uv(custom_path=custom_path)
|
246
|
+
else:
|
247
|
+
return self.sequence_controller.get_data(custom_path=custom_path)
|
232
248
|
|
233
249
|
def check_loaded_sequence(self) -> str:
|
234
250
|
"""Returns the name of the currently loaded sequence."""
|
@@ -0,0 +1,274 @@
|
|
1
|
+
import os
|
2
|
+
import time
|
3
|
+
from typing import Optional, Any
|
4
|
+
|
5
|
+
from xsdata.formats.dataclass.parsers import XmlParser
|
6
|
+
|
7
|
+
from .. import CommunicationController
|
8
|
+
from ...control.table.table_controller import TableController
|
9
|
+
from ...generated import PumpMethod, DadMethod, SolventElement
|
10
|
+
from ...utils.chromatogram import TIME_FORMAT, AgilentHPLCChromatogram
|
11
|
+
from ...utils.macro import Command
|
12
|
+
from ...utils.method_types import PType, TimeTableEntry, Param, MethodTimetable, HPLCMethodParams
|
13
|
+
from ...utils.table_types import RegisterFlag, TableOperation, Table
|
14
|
+
|
15
|
+
|
16
|
+
class MethodController(TableController):
|
17
|
+
"""
|
18
|
+
Class containing method related logic
|
19
|
+
"""
|
20
|
+
|
21
|
+
def __init__(self, controller: CommunicationController, src: str, data_dir: str, table: Table):
|
22
|
+
super().__init__(controller, src, data_dir, table)
|
23
|
+
|
24
|
+
def get_method_params(self) -> HPLCMethodParams:
|
25
|
+
return HPLCMethodParams(organic_modifier=self.controller.get_num_val(
|
26
|
+
cmd=TableOperation.GET_OBJ_HDR_VAL.value.format(
|
27
|
+
register=self.table.register,
|
28
|
+
register_flag=RegisterFlag.SOLVENT_B_COMPOSITION
|
29
|
+
)
|
30
|
+
),
|
31
|
+
flow=self.controller.get_num_val(
|
32
|
+
cmd=TableOperation.GET_OBJ_HDR_VAL.value.format(
|
33
|
+
register=self.table.register,
|
34
|
+
register_flag=RegisterFlag.FLOW
|
35
|
+
)
|
36
|
+
),
|
37
|
+
maximum_run_time=self.controller.get_num_val(
|
38
|
+
cmd=TableOperation.GET_OBJ_HDR_VAL.value.format(
|
39
|
+
register=self.table.register,
|
40
|
+
register_flag=RegisterFlag.MAX_TIME
|
41
|
+
)
|
42
|
+
),
|
43
|
+
)
|
44
|
+
|
45
|
+
def get_row(self, row: int) -> TimeTableEntry:
|
46
|
+
return TimeTableEntry(start_time=self.get_num(row, RegisterFlag.TIME),
|
47
|
+
organic_modifer=self.get_num(row, RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION),
|
48
|
+
flow=None)
|
49
|
+
|
50
|
+
def load(self) -> MethodTimetable:
|
51
|
+
rows = self.get_num_rows()
|
52
|
+
if rows.is_ok():
|
53
|
+
timetable_rows = [self.get_row(r + 1) for r in range(int(rows.ok_value.num_response))]
|
54
|
+
return MethodTimetable(
|
55
|
+
first_row=self.get_method_params(),
|
56
|
+
subsequent_rows=timetable_rows)
|
57
|
+
else:
|
58
|
+
raise RuntimeError(rows.err_value)
|
59
|
+
|
60
|
+
def current_method(self, method_name: str):
|
61
|
+
"""
|
62
|
+
Checks if a given method is already loaded into Chemstation. Method name does not need the ".M" extension.
|
63
|
+
|
64
|
+
:param method_name: a Chemstation method
|
65
|
+
:return: True if method is already loaded
|
66
|
+
"""
|
67
|
+
self.send(Command.GET_METHOD_CMD)
|
68
|
+
parsed_response = self.receive()
|
69
|
+
return method_name in parsed_response
|
70
|
+
|
71
|
+
def switch(self, method_name: str):
|
72
|
+
"""
|
73
|
+
Allows the user to switch between pre-programmed methods. No need to append '.M'
|
74
|
+
to the end of the method name. For example. for the method named 'General-Poroshell.M',
|
75
|
+
only 'General-Poroshell' is needed.
|
76
|
+
|
77
|
+
:param method_name: any available method in Chemstation method directory
|
78
|
+
:raise IndexError: Response did not have expected format. Try again.
|
79
|
+
:raise AssertionError: The desired method is not selected. Try again.
|
80
|
+
"""
|
81
|
+
self.send(Command.SWITCH_METHOD_CMD.value.format(method_dir=self.src,
|
82
|
+
method_name=method_name))
|
83
|
+
|
84
|
+
time.sleep(2)
|
85
|
+
self.send(Command.GET_METHOD_CMD)
|
86
|
+
time.sleep(2)
|
87
|
+
res = self.receive()
|
88
|
+
if res.is_ok():
|
89
|
+
parsed_response = res.ok_value.string_response
|
90
|
+
assert parsed_response == f"{method_name}.M", "Switching Methods failed."
|
91
|
+
|
92
|
+
def load_from_disk(self, method_name: str) -> MethodTimetable:
|
93
|
+
"""
|
94
|
+
Retrieve method details of an existing method. Don't need to append ".M" to the end. This assumes the
|
95
|
+
organic modifier is in Channel B and that Channel A contains the aq layer. Additionally, assumes
|
96
|
+
only two solvents are being used.
|
97
|
+
|
98
|
+
:param method_name: name of method to load details of
|
99
|
+
:raises FileNotFoundError: Method does not exist
|
100
|
+
:return: method details
|
101
|
+
"""
|
102
|
+
method_folder = f"{method_name}.M"
|
103
|
+
method_path = os.path.join(self.src, method_folder, "AgilentPumpDriver1.RapidControl.MethodXML.xml")
|
104
|
+
dad_path = os.path.join(self.src, method_folder, "Agilent1200erDadDriver1.RapidControl.MethodXML.xml")
|
105
|
+
|
106
|
+
if os.path.exists(os.path.join(self.src, f"{method_name}.M")):
|
107
|
+
parser = XmlParser()
|
108
|
+
method = parser.parse(method_path, PumpMethod)
|
109
|
+
dad = parser.parse(dad_path, DadMethod)
|
110
|
+
|
111
|
+
organic_modifier: Optional[SolventElement] = None
|
112
|
+
aq_modifier: Optional[SolventElement] = None
|
113
|
+
|
114
|
+
if len(method.solvent_composition.solvent_element) == 2:
|
115
|
+
for solvent in method.solvent_composition.solvent_element:
|
116
|
+
if solvent.channel == "Channel_A":
|
117
|
+
aq_modifier = solvent
|
118
|
+
elif solvent.channel == "Channel_B":
|
119
|
+
organic_modifier = solvent
|
120
|
+
|
121
|
+
return MethodTimetable(
|
122
|
+
first_row=HPLCMethodParams(
|
123
|
+
organic_modifier=organic_modifier.percentage,
|
124
|
+
flow=method.flow,
|
125
|
+
maximum_run_time=method.stop_time,
|
126
|
+
temperature=-1),
|
127
|
+
subsequent_rows=[
|
128
|
+
TimeTableEntry(
|
129
|
+
start_time=tte.time,
|
130
|
+
organic_modifer=tte.percent_b,
|
131
|
+
flow=method.flow
|
132
|
+
) for tte in method.timetable.timetable_entry
|
133
|
+
],
|
134
|
+
dad_wavelengthes=dad.signals.signal,
|
135
|
+
organic_modifier=organic_modifier,
|
136
|
+
modifier_a=aq_modifier
|
137
|
+
)
|
138
|
+
else:
|
139
|
+
raise FileNotFoundError
|
140
|
+
|
141
|
+
def edit(self, updated_method: MethodTimetable, save: bool):
|
142
|
+
"""Updated the currently loaded method in ChemStation with provided values.
|
143
|
+
|
144
|
+
:param updated_method: the method with updated values, to be sent to Chemstation to modify the currently loaded method.
|
145
|
+
"""
|
146
|
+
initial_organic_modifier: Param = Param(val=updated_method.first_row.organic_modifier,
|
147
|
+
chemstation_key=RegisterFlag.SOLVENT_B_COMPOSITION,
|
148
|
+
ptype=PType.NUM)
|
149
|
+
max_time: Param = Param(val=updated_method.first_row.maximum_run_time,
|
150
|
+
chemstation_key=RegisterFlag.MAX_TIME,
|
151
|
+
ptype=PType.NUM)
|
152
|
+
flow: Param = Param(val=updated_method.first_row.flow,
|
153
|
+
chemstation_key=RegisterFlag.FLOW,
|
154
|
+
ptype=PType.NUM)
|
155
|
+
|
156
|
+
# Method settings required for all runs
|
157
|
+
self.delete_table()
|
158
|
+
self._update_param(initial_organic_modifier)
|
159
|
+
self._update_param(flow)
|
160
|
+
self._update_param(Param(val="Set",
|
161
|
+
chemstation_key=RegisterFlag.STOPTIME_MODE,
|
162
|
+
ptype=PType.STR))
|
163
|
+
self._update_param(max_time)
|
164
|
+
self._update_param(Param(val="Off",
|
165
|
+
chemstation_key=RegisterFlag.POSTIME_MODE,
|
166
|
+
ptype=PType.STR))
|
167
|
+
|
168
|
+
self.send("DownloadRCMethod PMP1")
|
169
|
+
|
170
|
+
self._update_method_timetable(updated_method.subsequent_rows)
|
171
|
+
|
172
|
+
if save:
|
173
|
+
self.send(Command.SAVE_METHOD_CMD.value.format(
|
174
|
+
commit_msg=f"saved method at {str(time.time())}"
|
175
|
+
))
|
176
|
+
|
177
|
+
def _update_method_timetable(self, timetable_rows: list[TimeTableEntry]):
|
178
|
+
self.sleepy_send('Local Rows')
|
179
|
+
self.get_num_rows()
|
180
|
+
|
181
|
+
self.sleepy_send('DelTab RCPMP1Method[1], "Timetable"')
|
182
|
+
res = self.get_num_rows()
|
183
|
+
while not res.is_err():
|
184
|
+
self.sleepy_send('DelTab RCPMP1Method[1], "Timetable"')
|
185
|
+
res = self.get_num_rows()
|
186
|
+
|
187
|
+
self.sleepy_send('NewTab RCPMP1Method[1], "Timetable"')
|
188
|
+
self.get_num_rows()
|
189
|
+
|
190
|
+
for i, row in enumerate(timetable_rows):
|
191
|
+
if i == 0:
|
192
|
+
self.send('Sleep 1')
|
193
|
+
self.sleepy_send('InsTabRow RCPMP1Method[1], "Timetable"')
|
194
|
+
self.send('Sleep 1')
|
195
|
+
|
196
|
+
self.sleepy_send('NewColText RCPMP1Method[1], "Timetable", "Function", "SolventComposition"')
|
197
|
+
self.sleepy_send(f'NewColVal RCPMP1Method[1], "Timetable", "Time", {row.start_time}')
|
198
|
+
self.sleepy_send(
|
199
|
+
f'NewColVal RCPMP1Method[1], "Timetable", "SolventCompositionPumpChannel2_Percentage", {row.organic_modifer}')
|
200
|
+
|
201
|
+
self.send('Sleep 1')
|
202
|
+
self.sleepy_send("DownloadRCMethod PMP1")
|
203
|
+
self.send('Sleep 1')
|
204
|
+
else:
|
205
|
+
self.sleepy_send('InsTabRow RCPMP1Method[1], "Timetable"')
|
206
|
+
self.get_num_rows()
|
207
|
+
|
208
|
+
self.sleepy_send(
|
209
|
+
f'SetTabText RCPMP1Method[1], "Timetable", Rows, "Function", "SolventComposition"')
|
210
|
+
self.sleepy_send(
|
211
|
+
f'SetTabVal RCPMP1Method[1], "Timetable", Rows, "Time", {row.start_time}')
|
212
|
+
self.sleepy_send(
|
213
|
+
f'SetTabVal RCPMP1Method[1], "Timetable", Rows, "SolventCompositionPumpChannel2_Percentage", {row.organic_modifer}')
|
214
|
+
|
215
|
+
self.send("Sleep 1")
|
216
|
+
self.sleepy_send("DownloadRCMethod PMP1")
|
217
|
+
self.send("Sleep 1")
|
218
|
+
self.get_num_rows()
|
219
|
+
|
220
|
+
def _update_param(self, method_param: Param):
|
221
|
+
"""Change a method parameter, changes what is visibly seen in Chemstation GUI.
|
222
|
+
(changes the first row in the timetable)
|
223
|
+
|
224
|
+
:param method_param: a parameter to update for currently loaded method.
|
225
|
+
"""
|
226
|
+
register = self.table.register
|
227
|
+
setting_command = TableOperation.UPDATE_OBJ_HDR_VAL if method_param.ptype == PType.NUM else TableOperation.UPDATE_OBJ_HDR_TEXT
|
228
|
+
if isinstance(method_param.chemstation_key, list):
|
229
|
+
for register_flag in method_param.chemstation_key:
|
230
|
+
self.send(setting_command.value.format(register=register,
|
231
|
+
register_flag=register_flag,
|
232
|
+
val=method_param.val))
|
233
|
+
else:
|
234
|
+
self.send(setting_command.value.format(register=register,
|
235
|
+
register_flag=method_param.chemstation_key,
|
236
|
+
val=method_param.val))
|
237
|
+
time.sleep(2)
|
238
|
+
|
239
|
+
def stop(self):
|
240
|
+
"""
|
241
|
+
Stops the method run. A dialog window will pop up and manual intervention may be required.\
|
242
|
+
"""
|
243
|
+
self.send(Command.STOP_METHOD_CMD)
|
244
|
+
|
245
|
+
def run(self, experiment_name: str):
|
246
|
+
"""
|
247
|
+
This is the preferred method to trigger a run.
|
248
|
+
Starts the currently selected method, storing data
|
249
|
+
under the <data_dir>/<experiment_name>.D folder.
|
250
|
+
The <experiment_name> will be appended with a timestamp in the '%Y-%m-%d-%H-%M' format.
|
251
|
+
Device must be ready.
|
252
|
+
|
253
|
+
:param experiment_name: Name of the experiment
|
254
|
+
"""
|
255
|
+
timestamp = time.strftime(TIME_FORMAT)
|
256
|
+
self.send(Command.RUN_METHOD_CMD.value.format(data_dir=self.data_dir,
|
257
|
+
experiment_name=experiment_name,
|
258
|
+
timestamp=timestamp))
|
259
|
+
|
260
|
+
if self.check_hplc_is_running():
|
261
|
+
folder_name = f"{experiment_name}_{timestamp}.D"
|
262
|
+
self.data_files.append(os.path.join(self.data_dir, folder_name))
|
263
|
+
|
264
|
+
run_completed = self.check_hplc_done_running()
|
265
|
+
|
266
|
+
if not run_completed.is_ok():
|
267
|
+
raise RuntimeError("Run did not complete as expected")
|
268
|
+
|
269
|
+
def retrieve_recent_data_files(self) -> str:
|
270
|
+
return self.data_files[-1]
|
271
|
+
|
272
|
+
def get_data(self) -> dict[str, AgilentHPLCChromatogram]:
|
273
|
+
self.get_spectrum(self.data_files[-1])
|
274
|
+
return self.spectra
|