pychemstation 0.7.0.dev1__py3-none-any.whl → 0.8.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.
Files changed (49) hide show
  1. pychemstation/analysis/base_spectrum.py +3 -6
  2. pychemstation/analysis/process_report.py +248 -225
  3. pychemstation/analysis/utils.py +3 -1
  4. pychemstation/control/README.md +124 -0
  5. pychemstation/control/controllers/README.md +1 -0
  6. pychemstation/control/controllers/__init__.py +0 -2
  7. pychemstation/control/controllers/comm.py +27 -20
  8. pychemstation/control/controllers/devices/device.py +17 -4
  9. pychemstation/control/controllers/tables/method.py +57 -39
  10. pychemstation/control/controllers/tables/sequence.py +98 -31
  11. pychemstation/control/controllers/tables/table.py +121 -126
  12. pychemstation/control/hplc.py +82 -37
  13. pychemstation/generated/dad_method.py +3 -3
  14. pychemstation/generated/pump_method.py +7 -7
  15. pychemstation/out.txt +145 -0
  16. pychemstation/tests.ipynb +310 -0
  17. pychemstation/utils/chromatogram.py +5 -1
  18. pychemstation/utils/injector_types.py +2 -2
  19. pychemstation/utils/macro.py +1 -1
  20. pychemstation/utils/table_types.py +3 -0
  21. pychemstation/utils/tray_types.py +59 -39
  22. {pychemstation-0.7.0.dev1.dist-info → pychemstation-0.8.0.dist-info}/METADATA +25 -21
  23. pychemstation-0.8.0.dist-info/RECORD +39 -0
  24. {pychemstation-0.7.0.dev1.dist-info → pychemstation-0.8.0.dist-info}/WHEEL +1 -2
  25. pychemstation/control/comm.py +0 -206
  26. pychemstation/control/controllers/devices/column.py +0 -12
  27. pychemstation/control/controllers/devices/dad.py +0 -0
  28. pychemstation/control/controllers/devices/pump.py +0 -43
  29. pychemstation/control/controllers/method.py +0 -338
  30. pychemstation/control/controllers/sequence.py +0 -190
  31. pychemstation/control/controllers/table_controller.py +0 -266
  32. pychemstation/control/table/__init__.py +0 -3
  33. pychemstation/control/table/method.py +0 -274
  34. pychemstation/control/table/sequence.py +0 -210
  35. pychemstation/control/table/table_controller.py +0 -201
  36. pychemstation-0.7.0.dev1.dist-info/RECORD +0 -58
  37. pychemstation-0.7.0.dev1.dist-info/top_level.txt +0 -2
  38. tests/__init__.py +0 -0
  39. tests/constants.py +0 -88
  40. tests/test_comb.py +0 -136
  41. tests/test_comm.py +0 -65
  42. tests/test_inj.py +0 -39
  43. tests/test_method.py +0 -99
  44. tests/test_nightly.py +0 -80
  45. tests/test_proc_rep.py +0 -52
  46. tests/test_runs_stable.py +0 -125
  47. tests/test_sequence.py +0 -125
  48. tests/test_stable.py +0 -283
  49. {pychemstation-0.7.0.dev1.dist-info → pychemstation-0.8.0.dist-info/licenses}/LICENSE +0 -0
@@ -1,266 +0,0 @@
1
- """
2
- Abstract module containing shared logic for Method and Sequence tables.
3
-
4
- Authors: Lucy Hao
5
- """
6
-
7
- import abc
8
- import os
9
- from typing import Union, Optional
10
-
11
- import polling
12
- from result import Result, Ok, Err
13
-
14
- from ...control.controllers.comm import CommunicationController
15
- from ...utils.chromatogram import AgilentHPLCChromatogram, AgilentChannelChromatogramData
16
- from ...utils.macro import Command, HPLCRunningStatus, Response
17
- from ...utils.method_types import MethodDetails
18
- from ...utils.sequence_types import SequenceDataFiles, SequenceTable
19
- from ...utils.table_types import Table, TableOperation, RegisterFlag
20
-
21
- TableType = Union[MethodDetails, SequenceTable]
22
-
23
-
24
- class TableController(abc.ABC):
25
-
26
- def __init__(self, controller: CommunicationController, src: str, data_dir: str, table: Table):
27
- self.controller = controller
28
- self.table = table
29
- self.table_state: Optional[TableType] = None
30
-
31
- if os.path.isdir(src):
32
- self.src: str = src
33
- else:
34
- raise FileNotFoundError(f"dir: {src} not found.")
35
-
36
- if os.path.isdir(data_dir):
37
- self.data_dir: str = data_dir
38
- else:
39
- raise FileNotFoundError(f"dir: {data_dir} not found.")
40
-
41
- self.spectra: dict[str, Optional[AgilentHPLCChromatogram]] = {
42
- "A": AgilentHPLCChromatogram(self.data_dir),
43
- "B": AgilentHPLCChromatogram(self.data_dir),
44
- "C": AgilentHPLCChromatogram(self.data_dir),
45
- "D": AgilentHPLCChromatogram(self.data_dir),
46
- "E": AgilentHPLCChromatogram(self.data_dir),
47
- "F": AgilentHPLCChromatogram(self.data_dir),
48
- "G": AgilentHPLCChromatogram(self.data_dir),
49
- "H": AgilentHPLCChromatogram(self.data_dir),
50
- }
51
-
52
- self.data_files: Union[list[SequenceDataFiles], list[str]] = []
53
-
54
- # Initialize row counter for table operations
55
- self.send('Local Rows')
56
-
57
- def receive(self) -> Result[Response, str]:
58
- for _ in range(10):
59
- try:
60
- return self.controller.receive()
61
- except IndexError:
62
- continue
63
- return Err("Could not parse response")
64
-
65
- def send(self, cmd: Union[Command, str]):
66
- if not self.controller:
67
- raise RuntimeError(
68
- "Communication controller must be initialized before sending command. It is currently in offline mode.")
69
- self.controller.send(cmd)
70
-
71
- def sleepy_send(self, cmd: Union[Command, str]):
72
- self.controller.sleepy_send(cmd)
73
-
74
- def sleep(self, seconds: int):
75
- """
76
- Tells the HPLC to wait for a specified number of seconds.
77
-
78
- :param seconds: number of seconds to wait
79
- """
80
- self.send(Command.SLEEP_CMD.value.format(seconds=seconds))
81
-
82
- def get_num(self, row: int, col_name: RegisterFlag) -> float:
83
- return self.controller.get_num_val(TableOperation.GET_ROW_VAL.value.format(register=self.table.register,
84
- table_name=self.table.name,
85
- row=row,
86
- col_name=col_name.value))
87
-
88
- def get_text(self, row: int, col_name: RegisterFlag) -> str:
89
- return self.controller.get_text_val(TableOperation.GET_ROW_TEXT.value.format(register=self.table.register,
90
- table_name=self.table.name,
91
- row=row,
92
- col_name=col_name.value))
93
-
94
- def add_new_col_num(self,
95
- col_name: RegisterFlag,
96
- val: Union[int, float]):
97
- self.sleepy_send(TableOperation.NEW_COL_VAL.value.format(
98
- register=self.table.register,
99
- table_name=self.table.name,
100
- col_name=col_name,
101
- val=val))
102
-
103
- def add_new_col_text(self,
104
- col_name: RegisterFlag,
105
- val: str):
106
- self.sleepy_send(TableOperation.NEW_COL_TEXT.value.format(
107
- register=self.table.register,
108
- table_name=self.table.name,
109
- col_name=col_name,
110
- val=val))
111
-
112
- def edit_row_num(self,
113
- col_name: RegisterFlag,
114
- val: Union[int, float],
115
- row: Optional[int] = None):
116
- self.sleepy_send(TableOperation.EDIT_ROW_VAL.value.format(
117
- register=self.table.register,
118
- table_name=self.table.name,
119
- row=row if row is not None else 'Rows',
120
- col_name=col_name,
121
- val=val))
122
-
123
- def edit_row_text(self,
124
- col_name: RegisterFlag,
125
- val: str,
126
- row: Optional[int] = None):
127
- self.sleepy_send(TableOperation.EDIT_ROW_TEXT.value.format(
128
- register=self.table.register,
129
- table_name=self.table.name,
130
- row=row if row is not None else 'Rows',
131
- col_name=col_name,
132
- val=val))
133
-
134
- @abc.abstractmethod
135
- def get_row(self, row: int):
136
- pass
137
-
138
- def delete_row(self, row: int):
139
- self.sleepy_send(TableOperation.DELETE_ROW.value.format(register=self.table.register,
140
- table_name=self.table.name,
141
- row=row))
142
-
143
- def add_row(self):
144
- """
145
- Adds a row to the provided table for currently loaded method or sequence.
146
- Import either the SEQUENCE_TABLE or METHOD_TIMETABLE from hein_analytical_control.constants.
147
- You can also provide your own table.
148
-
149
- :param table: the table to add a new row to
150
- """
151
- self.sleepy_send(TableOperation.NEW_ROW.value.format(register=self.table.register,
152
- table_name=self.table.name))
153
-
154
- def delete_table(self):
155
- """
156
- Deletes the table for the current loaded method or sequence.
157
- Import either the SEQUENCE_TABLE or METHOD_TIMETABLE from hein_analytical_control.constants.
158
- You can also provide your own table.
159
-
160
- :param table: the table to delete
161
- """
162
- self.sleepy_send(TableOperation.DELETE_TABLE.value.format(register=self.table.register,
163
- table_name=self.table.name))
164
-
165
- def new_table(self):
166
- """
167
- Creates the table for the currently loaded method or sequence. Import either the SEQUENCE_TABLE or
168
- METHOD_TIMETABLE from hein_analytical_control.constants. You can also provide your own table.
169
-
170
- :param table: the table to create
171
- """
172
- self.send(TableOperation.CREATE_TABLE.value.format(register=self.table.register,
173
- table_name=self.table.name))
174
-
175
- def get_num_rows(self) -> Result[Response, str]:
176
- self.send(TableOperation.GET_NUM_ROWS.value.format(register=self.table.register,
177
- table_name=self.table.name,
178
- col_name=RegisterFlag.NUM_ROWS))
179
- self.send(Command.GET_ROWS_CMD.value.format(register=self.table.register,
180
- table_name=self.table.name,
181
- col_name=RegisterFlag.NUM_ROWS))
182
- res = self.controller.receive()
183
-
184
- if res.is_ok():
185
- self.send("Sleep 0.1")
186
- self.send('Print Rows')
187
- return res
188
- else:
189
- return Err("No rows could be read.")
190
-
191
- def check_hplc_is_running(self) -> bool:
192
- started_running = polling.poll(
193
- lambda: isinstance(self.controller.get_status(), HPLCRunningStatus),
194
- step=5,
195
- max_tries=100)
196
- return started_running
197
-
198
- def check_hplc_done_running(self,
199
- method: Optional[MethodDetails] = None,
200
- sequence: Optional[SequenceTable] = None) -> Result[str, str]:
201
- """
202
- Checks if ChemStation has finished running and can read data back
203
-
204
- :param method: if you are running a method and want to read back data, the timeout period will be adjusted to be longer than the method's runtime
205
- :param sequence: if you are running a sequence and want to read back data, the timeout period will be adjusted to be longer than the sequence's runtime
206
- :return: Return True if data can be read back, else False.
207
- """
208
- timeout = 10 * 60
209
- if method:
210
- timeout = ((method.stop_time + method.post_time + 3) * 60)
211
- if sequence:
212
- timeout *= len(sequence.rows)
213
-
214
- most_recent_folder = self.retrieve_recent_data_files()
215
- finished_run = polling.poll(
216
- lambda: self.controller.check_if_running(),
217
- timeout=timeout,
218
- step=50)
219
-
220
- check_folder = self.fuzzy_match_most_recent_folder(most_recent_folder)
221
- if check_folder.is_ok() and finished_run:
222
- return check_folder
223
- elif check_folder.is_ok():
224
- finished_run = polling.poll(
225
- lambda: self.controller.check_if_running(),
226
- timeout=timeout,
227
- step=50)
228
- if finished_run:
229
- return check_folder
230
- return check_folder
231
- else:
232
- return Err("Run did not complete as expected")
233
-
234
- def fuzzy_match_most_recent_folder(self, most_recent_folder) -> Result[str, str]:
235
- if os.path.exists(most_recent_folder):
236
- return Ok(most_recent_folder)
237
-
238
- subdirs = [x[0] for x in os.walk(self.data_dir)]
239
- potential_folders = sorted(list(filter(lambda d: most_recent_folder in d, subdirs)))
240
- parent_dirs = []
241
- for folder in potential_folders:
242
- path = os.path.normpath(folder)
243
- split_folder = path.split(os.sep)
244
- if most_recent_folder in split_folder[-1]:
245
- parent_dirs.append(folder)
246
- parent_dir = sorted(parent_dirs, reverse=True)[0]
247
- return Ok(parent_dir)
248
-
249
- @abc.abstractmethod
250
- def retrieve_recent_data_files(self):
251
- pass
252
-
253
- @abc.abstractmethod
254
- def get_data(self) -> Union[list[AgilentChannelChromatogramData], AgilentChannelChromatogramData]:
255
- pass
256
-
257
- def get_spectrum(self, data_file: str):
258
- """
259
- Load chromatogram for any channel in spectra dictionary.
260
- """
261
- for channel, spec in self.spectra.items():
262
- try:
263
- spec.load_spectrum(data_path=data_file, channel=channel)
264
- except FileNotFoundError:
265
- self.spectra[channel] = None
266
- print(f"No data at channel: {channel}")
@@ -1,3 +0,0 @@
1
- from .method import MethodController
2
- from .sequence import SequenceController
3
- from .table_controller import TableController
@@ -1,274 +0,0 @@
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