pychemstation 0.10.2__py3-none-any.whl → 0.10.4__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/__init__.py +1 -1
- pychemstation/analysis/__init__.py +3 -4
- pychemstation/analysis/base_spectrum.py +7 -7
- pychemstation/{utils → analysis}/chromatogram.py +4 -4
- pychemstation/analysis/process_report.py +106 -70
- pychemstation/control/README.md +21 -53
- pychemstation/control/__init__.py +3 -2
- pychemstation/control/controllers/__init__.py +1 -5
- pychemstation/control/controllers/comm.py +20 -11
- pychemstation/control/controllers/devices/device.py +22 -12
- pychemstation/control/controllers/devices/injector.py +24 -14
- pychemstation/control/controllers/tables/method.py +233 -100
- pychemstation/control/controllers/tables/ms.py +7 -4
- pychemstation/control/controllers/tables/sequence.py +134 -54
- pychemstation/control/controllers/tables/table.py +152 -92
- pychemstation/control/hplc.py +96 -78
- pychemstation/generated/__init__.py +0 -2
- pychemstation/generated/pump_method.py +15 -19
- pychemstation/utils/macro.py +10 -9
- pychemstation/utils/method_types.py +1 -0
- pychemstation/utils/num_utils.py +2 -2
- pychemstation/utils/parsing.py +0 -11
- pychemstation/utils/sequence_types.py +2 -3
- pychemstation/utils/spec_utils.py +2 -3
- pychemstation/utils/table_types.py +10 -9
- pychemstation/utils/tray_types.py +48 -38
- {pychemstation-0.10.2.dist-info → pychemstation-0.10.4.dist-info}/METADATA +46 -20
- pychemstation-0.10.4.dist-info/RECORD +37 -0
- pychemstation-0.10.2.dist-info/RECORD +0 -37
- {pychemstation-0.10.2.dist-info → pychemstation-0.10.4.dist-info}/WHEEL +0 -0
- {pychemstation-0.10.2.dist-info → pychemstation-0.10.4.dist-info}/licenses/LICENSE +0 -0
@@ -3,6 +3,7 @@ Abstract module containing shared logic for Method and Sequence tables.
|
|
3
3
|
|
4
4
|
Authors: Lucy Hao
|
5
5
|
"""
|
6
|
+
|
6
7
|
from __future__ import annotations
|
7
8
|
|
8
9
|
import abc
|
@@ -23,25 +24,27 @@ from ....analysis.process_report import (
|
|
23
24
|
TXTProcessor,
|
24
25
|
)
|
25
26
|
from ....control.controllers.comm import CommunicationController
|
26
|
-
from
|
27
|
+
from pychemstation.analysis.chromatogram import (
|
27
28
|
AgilentChannelChromatogramData,
|
28
29
|
AgilentHPLCChromatogram,
|
29
30
|
)
|
30
31
|
from ....utils.macro import Command, HPLCRunningStatus, Response
|
31
32
|
from ....utils.method_types import MethodDetails
|
32
|
-
from ....utils.sequence_types import
|
33
|
+
from ....utils.sequence_types import SequenceTable
|
33
34
|
from ....utils.table_types import RegisterFlag, T, Table, TableOperation
|
34
35
|
|
35
36
|
TableType = Union[MethodDetails, SequenceTable]
|
36
37
|
|
37
38
|
|
38
39
|
class TableController(abc.ABC):
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
def __init__(
|
41
|
+
self,
|
42
|
+
controller: CommunicationController,
|
43
|
+
src: Optional[str],
|
44
|
+
data_dirs: Optional[List[str]],
|
45
|
+
table: Table,
|
46
|
+
offline: bool = False,
|
47
|
+
):
|
45
48
|
self.controller = controller
|
46
49
|
self.table_locator = table
|
47
50
|
self.table_state: Optional[TableType] = None
|
@@ -71,7 +74,7 @@ class TableController(abc.ABC):
|
|
71
74
|
"H": AgilentHPLCChromatogram(),
|
72
75
|
}
|
73
76
|
self.report: Optional[AgilentReport] = None
|
74
|
-
self.uv: Dict[
|
77
|
+
self.uv: Dict[int, AgilentHPLCChromatogram] = {}
|
75
78
|
self.data_files: List = []
|
76
79
|
|
77
80
|
def receive(self) -> Result[Response, str]:
|
@@ -85,7 +88,8 @@ class TableController(abc.ABC):
|
|
85
88
|
def send(self, cmd: Union[Command, str]):
|
86
89
|
if not self.controller:
|
87
90
|
raise RuntimeError(
|
88
|
-
"Communication controller must be initialized before sending command. It is currently in offline mode."
|
91
|
+
"Communication controller must be initialized before sending command. It is currently in offline mode."
|
92
|
+
)
|
89
93
|
self.controller.send(cmd)
|
90
94
|
|
91
95
|
def sleepy_send(self, cmd: Union[Command, str]):
|
@@ -100,108 +104,145 @@ class TableController(abc.ABC):
|
|
100
104
|
self.send(Command.SLEEP_CMD.value.format(seconds=seconds))
|
101
105
|
|
102
106
|
def get_num(self, row: int, col_name: RegisterFlag) -> Union[int, float]:
|
103
|
-
return self.controller.get_num_val(
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
+
return self.controller.get_num_val(
|
108
|
+
TableOperation.GET_ROW_VAL.value.format(
|
109
|
+
register=self.table_locator.register,
|
110
|
+
table_name=self.table_locator.name,
|
111
|
+
row=row,
|
112
|
+
col_name=col_name.value,
|
113
|
+
)
|
114
|
+
)
|
107
115
|
|
108
116
|
def get_text(self, row: int, col_name: RegisterFlag) -> str:
|
109
117
|
return self.controller.get_text_val(
|
110
|
-
TableOperation.GET_ROW_TEXT.value.format(
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
118
|
+
TableOperation.GET_ROW_TEXT.value.format(
|
119
|
+
register=self.table_locator.register,
|
120
|
+
table_name=self.table_locator.name,
|
121
|
+
row=row,
|
122
|
+
col_name=col_name.value,
|
123
|
+
)
|
124
|
+
)
|
125
|
+
|
126
|
+
def add_new_col_num(self, col_name: RegisterFlag, val: Union[int, float]):
|
127
|
+
self.sleepy_send(
|
128
|
+
TableOperation.NEW_COL_VAL.value.format(
|
129
|
+
register=self.table_locator.register,
|
130
|
+
table_name=self.table_locator.name,
|
131
|
+
col_name=col_name,
|
132
|
+
val=val,
|
133
|
+
)
|
134
|
+
)
|
135
|
+
|
136
|
+
def add_new_col_text(self, col_name: RegisterFlag, val: str):
|
137
|
+
self.sleepy_send(
|
138
|
+
TableOperation.NEW_COL_TEXT.value.format(
|
139
|
+
register=self.table_locator.register,
|
140
|
+
table_name=self.table_locator.name,
|
141
|
+
col_name=col_name,
|
142
|
+
val=val,
|
143
|
+
)
|
144
|
+
)
|
145
|
+
|
146
|
+
def _edit_row_num(
|
147
|
+
self, col_name: RegisterFlag, val: Union[int, float], row: Optional[int] = None
|
148
|
+
):
|
149
|
+
self.sleepy_send(
|
150
|
+
TableOperation.EDIT_ROW_VAL.value.format(
|
151
|
+
register=self.table_locator.register,
|
152
|
+
table_name=self.table_locator.name,
|
153
|
+
row=row if row is not None else "Rows",
|
154
|
+
col_name=col_name,
|
155
|
+
val=val,
|
156
|
+
)
|
157
|
+
)
|
158
|
+
|
159
|
+
def _edit_row_text(
|
160
|
+
self, col_name: RegisterFlag, val: str, row: Optional[int] = None
|
161
|
+
):
|
162
|
+
self.sleepy_send(
|
163
|
+
TableOperation.EDIT_ROW_TEXT.value.format(
|
164
|
+
register=self.table_locator.register,
|
165
|
+
table_name=self.table_locator.name,
|
166
|
+
row=row if row is not None else "Rows",
|
167
|
+
col_name=col_name,
|
168
|
+
val=val,
|
169
|
+
)
|
170
|
+
)
|
154
171
|
|
155
172
|
@abc.abstractmethod
|
156
173
|
def get_row(self, row: int):
|
157
174
|
pass
|
158
175
|
|
159
176
|
def delete_row(self, row: int):
|
160
|
-
self.sleepy_send(
|
161
|
-
|
162
|
-
|
177
|
+
self.sleepy_send(
|
178
|
+
TableOperation.DELETE_ROW.value.format(
|
179
|
+
register=self.table_locator.register,
|
180
|
+
table_name=self.table_locator.name,
|
181
|
+
row=row,
|
182
|
+
)
|
183
|
+
)
|
163
184
|
|
164
185
|
def add_row(self):
|
165
186
|
"""
|
166
187
|
Adds a row to the provided table for currently loaded method or sequence.
|
167
188
|
"""
|
168
|
-
self.sleepy_send(
|
169
|
-
|
189
|
+
self.sleepy_send(
|
190
|
+
TableOperation.NEW_ROW.value.format(
|
191
|
+
register=self.table_locator.register, table_name=self.table_locator.name
|
192
|
+
)
|
193
|
+
)
|
170
194
|
|
171
195
|
def delete_table(self):
|
172
196
|
"""
|
173
197
|
Deletes the table for the current loaded method or sequence.
|
174
198
|
"""
|
175
|
-
self.sleepy_send(
|
176
|
-
|
199
|
+
self.sleepy_send(
|
200
|
+
TableOperation.DELETE_TABLE.value.format(
|
201
|
+
register=self.table_locator.register, table_name=self.table_locator.name
|
202
|
+
)
|
203
|
+
)
|
177
204
|
|
178
205
|
def new_table(self):
|
179
206
|
"""
|
180
207
|
Creates the table for the currently loaded method or sequence.
|
181
208
|
"""
|
182
|
-
self.send(
|
183
|
-
|
209
|
+
self.send(
|
210
|
+
TableOperation.CREATE_TABLE.value.format(
|
211
|
+
register=self.table_locator.register, table_name=self.table_locator.name
|
212
|
+
)
|
213
|
+
)
|
184
214
|
|
185
215
|
def get_num_rows(self) -> Result[Response, str]:
|
186
|
-
self.send(
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
216
|
+
self.send(
|
217
|
+
TableOperation.GET_NUM_ROWS.value.format(
|
218
|
+
register=self.table_locator.register,
|
219
|
+
table_name=self.table_locator.name,
|
220
|
+
col_name=RegisterFlag.NUM_ROWS,
|
221
|
+
)
|
222
|
+
)
|
223
|
+
self.send(
|
224
|
+
Command.GET_ROWS_CMD.value.format(
|
225
|
+
register=self.table_locator.register,
|
226
|
+
table_name=self.table_locator.name,
|
227
|
+
col_name=RegisterFlag.NUM_ROWS,
|
228
|
+
)
|
229
|
+
)
|
192
230
|
res = self.controller.receive()
|
193
231
|
|
194
232
|
if res.is_ok():
|
195
233
|
self.send("Sleep 0.1")
|
196
|
-
self.send(
|
234
|
+
self.send("Print Rows")
|
197
235
|
return res
|
198
236
|
else:
|
199
237
|
return Err("No rows could be read.")
|
200
238
|
|
201
239
|
def check_hplc_is_running(self) -> bool:
|
202
240
|
try:
|
203
|
-
started_running = polling.poll(
|
204
|
-
|
241
|
+
started_running = polling.poll(
|
242
|
+
lambda: isinstance(self.controller.get_status(), HPLCRunningStatus),
|
243
|
+
step=1,
|
244
|
+
max_tries=20,
|
245
|
+
)
|
205
246
|
except Exception as e:
|
206
247
|
print(e)
|
207
248
|
return False
|
@@ -212,7 +253,7 @@ class TableController(abc.ABC):
|
|
212
253
|
def check_hplc_run_finished(self) -> Tuple[float, bool]:
|
213
254
|
done_running = self.controller.check_if_not_running()
|
214
255
|
if self.curr_run_starting_time and self.timeout:
|
215
|
-
time_passed =
|
256
|
+
time_passed = time.time() - self.curr_run_starting_time
|
216
257
|
if time_passed > self.timeout:
|
217
258
|
enough_time_passed = time_passed >= self.timeout
|
218
259
|
run_finished = enough_time_passed and done_running
|
@@ -235,13 +276,25 @@ class TableController(abc.ABC):
|
|
235
276
|
try:
|
236
277
|
finished_run = not polling.poll(
|
237
278
|
lambda: self.check_hplc_run_finished()[1],
|
238
|
-
max_tries=minutes - 1,
|
239
|
-
|
279
|
+
max_tries=minutes - 1,
|
280
|
+
step=50,
|
281
|
+
)
|
282
|
+
except (
|
283
|
+
polling.TimeoutException,
|
284
|
+
polling.PollingException,
|
285
|
+
polling.MaxCallException,
|
286
|
+
):
|
240
287
|
try:
|
241
288
|
finished_run = polling.poll(
|
242
289
|
lambda: self.check_hplc_run_finished()[1],
|
243
|
-
timeout=self.timeout / 2,
|
244
|
-
|
290
|
+
timeout=self.timeout / 2,
|
291
|
+
step=1,
|
292
|
+
)
|
293
|
+
except (
|
294
|
+
polling.TimeoutException,
|
295
|
+
polling.PollingException,
|
296
|
+
polling.MaxCallException,
|
297
|
+
):
|
245
298
|
pass
|
246
299
|
|
247
300
|
check_folder = self.fuzzy_match_most_recent_folder(self.data_files[-1])
|
@@ -250,14 +303,13 @@ class TableController(abc.ABC):
|
|
250
303
|
elif check_folder.is_ok():
|
251
304
|
try:
|
252
305
|
finished_run = polling.poll(
|
253
|
-
lambda: self.check_hplc_run_finished()[1],
|
254
|
-
|
255
|
-
step=50)
|
306
|
+
lambda: self.check_hplc_run_finished()[1], max_tries=10, step=50
|
307
|
+
)
|
256
308
|
if finished_run:
|
257
309
|
return check_folder
|
258
310
|
except Exception:
|
259
311
|
self._reset_time()
|
260
|
-
return
|
312
|
+
return self.data_files[-1]
|
261
313
|
return Err("Run did not complete as expected")
|
262
314
|
|
263
315
|
@abc.abstractmethod
|
@@ -265,15 +317,23 @@ class TableController(abc.ABC):
|
|
265
317
|
pass
|
266
318
|
|
267
319
|
@abc.abstractmethod
|
268
|
-
def get_data(
|
320
|
+
def get_data(
|
321
|
+
self, custom_path: Optional[str] = None
|
322
|
+
) -> Union[List[AgilentChannelChromatogramData], AgilentChannelChromatogramData]:
|
269
323
|
pass
|
270
324
|
|
271
325
|
@abc.abstractmethod
|
272
|
-
def get_data_uv(
|
326
|
+
def get_data_uv(
|
327
|
+
self,
|
328
|
+
) -> Union[
|
329
|
+
List[Dict[str, AgilentHPLCChromatogram]], Dict[str, AgilentHPLCChromatogram]
|
330
|
+
]:
|
273
331
|
pass
|
274
332
|
|
275
333
|
@abc.abstractmethod
|
276
|
-
def get_report(
|
334
|
+
def get_report(
|
335
|
+
self, report_type: ReportType = ReportType.TXT
|
336
|
+
) -> List[AgilentReport]:
|
277
337
|
pass
|
278
338
|
|
279
339
|
def get_uv_spectrum(self, path: str):
|
@@ -281,12 +341,13 @@ class TableController(abc.ABC):
|
|
281
341
|
times = data_uv.xlabels
|
282
342
|
wavelengths = data_uv.ylabels
|
283
343
|
absorbances = data_uv.data.transpose()
|
284
|
-
for
|
344
|
+
for i, w in enumerate(wavelengths):
|
285
345
|
self.uv[w] = AgilentHPLCChromatogram()
|
286
346
|
self.uv[w].attach_spectrum(times, absorbances[i])
|
287
347
|
|
288
|
-
def get_report_details(
|
289
|
-
|
348
|
+
def get_report_details(
|
349
|
+
self, path: str, report_type: ReportType = ReportType.TXT
|
350
|
+
) -> AgilentReport:
|
290
351
|
if report_type is ReportType.TXT:
|
291
352
|
txt_report = TXTProcessor(path).process_report()
|
292
353
|
if txt_report.is_ok():
|
@@ -297,7 +358,6 @@ class TableController(abc.ABC):
|
|
297
358
|
self.report = csv_report.ok_value
|
298
359
|
return self.report
|
299
360
|
|
300
|
-
|
301
361
|
def get_spectrum_at_channels(self, data_path: str):
|
302
362
|
"""
|
303
363
|
Load chromatogram for any channel in spectra dictionary.
|