pychemstation 0.8.2__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 +47 -38
- 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.2.dist-info → pychemstation-0.10.0.dist-info}/METADATA +19 -7
- pychemstation-0.10.0.dist-info/RECORD +62 -0
- {pychemstation-0.8.2.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/out.txt +0 -145
- pychemstation/tests.ipynb +0 -310
- pychemstation-0.8.2.dist-info/RECORD +0 -39
- {pychemstation-0.8.2.dist-info → pychemstation-0.10.0.dist-info}/licenses/LICENSE +0 -0
@@ -6,12 +6,12 @@ from abc import ABC, abstractmethod
|
|
6
6
|
import matplotlib.pyplot as plt
|
7
7
|
import numpy as np
|
8
8
|
from scipy import (
|
9
|
-
sparse,
|
10
|
-
signal,
|
11
9
|
integrate,
|
10
|
+
signal,
|
11
|
+
sparse,
|
12
12
|
)
|
13
13
|
|
14
|
-
from .
|
14
|
+
from ..utils.num_utils import find_nearest_value_index, interpolate_to_index
|
15
15
|
|
16
16
|
|
17
17
|
class AbstractSpectrum(ABC):
|
@@ -197,10 +197,10 @@ class AbstractSpectrum(ABC):
|
|
197
197
|
return (self.x.copy()[full_mask], self.y.copy()[full_mask])
|
198
198
|
|
199
199
|
def show_spectrum(
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
200
|
+
self,
|
201
|
+
filename=None,
|
202
|
+
title=None,
|
203
|
+
label=None,
|
204
204
|
):
|
205
205
|
"""Plots the spectral data using matplotlib.pyplot module.
|
206
206
|
|
@@ -385,12 +385,12 @@ class AbstractSpectrum(ABC):
|
|
385
385
|
|
386
386
|
if rule == "trapz":
|
387
387
|
return integrate.trapz(
|
388
|
-
self.y[left_idx
|
388
|
+
self.y[left_idx: right_idx + 1], self.x[left_idx: right_idx + 1]
|
389
389
|
)
|
390
390
|
|
391
391
|
elif rule == "simps":
|
392
392
|
return integrate.simps(
|
393
|
-
self.y[left_idx
|
393
|
+
self.y[left_idx: right_idx + 1], self.x[left_idx: right_idx + 1]
|
394
394
|
)
|
395
395
|
|
396
396
|
else:
|
@@ -4,15 +4,21 @@ import re
|
|
4
4
|
from abc import abstractmethod
|
5
5
|
from dataclasses import dataclass
|
6
6
|
from enum import Enum
|
7
|
-
from typing import
|
7
|
+
from typing import AnyStr, Dict, List, Optional, Pattern
|
8
8
|
|
9
9
|
import pandas as pd
|
10
|
-
from aghplctools.ingestion.text import
|
11
|
-
|
12
|
-
|
10
|
+
from aghplctools.ingestion.text import (
|
11
|
+
_area_report_re,
|
12
|
+
_header_block_re,
|
13
|
+
_no_peaks_re,
|
14
|
+
_signal_info_re,
|
15
|
+
_signal_table_re,
|
16
|
+
chunk_string,
|
17
|
+
)
|
18
|
+
from result import Err, Ok, Result
|
13
19
|
|
14
20
|
from pychemstation.utils.chromatogram import AgilentHPLCChromatogram
|
15
|
-
from pychemstation.utils.tray_types import
|
21
|
+
from pychemstation.utils.tray_types import FiftyFourVialPlate, Tray
|
16
22
|
|
17
23
|
|
18
24
|
@dataclass
|
@@ -70,7 +76,7 @@ class CSVProcessor(ReportProcessor):
|
|
70
76
|
:returns: subset of complete report details, specifically the sample location, solvents in pumps,
|
71
77
|
and list of peaks at each wavelength channel.
|
72
78
|
"""
|
73
|
-
labels = os.path.join(self.path,
|
79
|
+
labels = os.path.join(self.path, 'REPORT00.CSV')
|
74
80
|
if os.path.exists(labels):
|
75
81
|
df_labels: Dict[int, Dict[int: AnyStr]] = pd.read_csv(labels, encoding="utf-16", header=None).to_dict()
|
76
82
|
vial_location = []
|
@@ -200,7 +206,7 @@ class TXTProcessor(ReportProcessor):
|
|
200
206
|
should be able to use the `parse_area_report` method of aghplctools v4.8.8
|
201
207
|
"""
|
202
208
|
if re.search(_no_peaks_re, report_text): # There are no peaks in Report.txt
|
203
|
-
raise ValueError(
|
209
|
+
raise ValueError('No peaks found in Report.txt')
|
204
210
|
blocks = _header_block_re.split(report_text)
|
205
211
|
signals = {} # output dictionary
|
206
212
|
for ind, block in enumerate(blocks):
|
pychemstation/analysis/utils.py
CHANGED
@@ -1,9 +1,7 @@
|
|
1
|
-
from typing import Tuple
|
2
|
-
|
3
1
|
import numpy as np
|
4
2
|
|
5
3
|
|
6
|
-
def find_nearest_value_index(array, value) ->
|
4
|
+
def find_nearest_value_index(array, value) -> tuple[float, int]:
|
7
5
|
"""Returns closest value and its index in a given array.
|
8
6
|
|
9
7
|
:param array: An array to search in.
|
@@ -0,0 +1,206 @@
|
|
1
|
+
"""
|
2
|
+
Module to provide API for the communication with Agilent HPLC systems.
|
3
|
+
|
4
|
+
HPLCController sends commands to Chemstation software via a command file.
|
5
|
+
Answers are received via reply file. On the Chemstation side, a custom
|
6
|
+
Macro monitors the command file, executes commands and writes to the reply file.
|
7
|
+
Each command is given a number (cmd_no) to keep track of which commands have
|
8
|
+
been processed.
|
9
|
+
|
10
|
+
Authors: Alexander Hammer, Hessam Mehr, Lucy Hao
|
11
|
+
"""
|
12
|
+
|
13
|
+
import os
|
14
|
+
import time
|
15
|
+
|
16
|
+
from result import Result, Ok, Err
|
17
|
+
from ..utils.macro import *
|
18
|
+
from ..utils.method_types import *
|
19
|
+
|
20
|
+
|
21
|
+
class CommunicationController:
|
22
|
+
"""
|
23
|
+
Class that communicates with Agilent using Macros
|
24
|
+
"""
|
25
|
+
|
26
|
+
# maximum command number
|
27
|
+
MAX_CMD_NO = 255
|
28
|
+
|
29
|
+
def __init__(
|
30
|
+
self,
|
31
|
+
comm_dir: str,
|
32
|
+
cmd_file: str = "cmd",
|
33
|
+
reply_file: str = "reply",
|
34
|
+
):
|
35
|
+
"""
|
36
|
+
:param comm_dir:
|
37
|
+
:param cmd_file: Name of command file
|
38
|
+
:param reply_file: Name of reply file
|
39
|
+
"""
|
40
|
+
if os.path.isdir(comm_dir):
|
41
|
+
self.cmd_file = os.path.join(comm_dir, cmd_file)
|
42
|
+
self.reply_file = os.path.join(comm_dir, reply_file)
|
43
|
+
self.cmd_no = 0
|
44
|
+
else:
|
45
|
+
raise FileNotFoundError(f"comm_dir: {comm_dir} not found.")
|
46
|
+
self._most_recent_hplc_status = None
|
47
|
+
|
48
|
+
# Create files for Chemstation to communicate with Python
|
49
|
+
open(self.cmd_file, "a").close()
|
50
|
+
open(self.reply_file, "a").close()
|
51
|
+
|
52
|
+
self.reset_cmd_counter()
|
53
|
+
|
54
|
+
def get_num_val(self, cmd: str) -> Union[int, float, Err]:
|
55
|
+
self.send(Command.GET_NUM_VAL_CMD.value.format(cmd=cmd))
|
56
|
+
res = self.receive()
|
57
|
+
if res.is_ok():
|
58
|
+
return res.ok_value.num_response
|
59
|
+
else:
|
60
|
+
raise RuntimeError("Failed to get number.")
|
61
|
+
|
62
|
+
def get_text_val(self, cmd: str) -> str:
|
63
|
+
self.send(Command.GET_TEXT_VAL_CMD.value.format(cmd=cmd))
|
64
|
+
res = self.receive()
|
65
|
+
if res.is_ok():
|
66
|
+
return res.ok_value.string_response
|
67
|
+
else:
|
68
|
+
raise RuntimeError("Failed to get string")
|
69
|
+
|
70
|
+
def get_status(self) -> Union[HPLCRunningStatus, HPLCAvailStatus, HPLCErrorStatus]:
|
71
|
+
"""Get device status(es).
|
72
|
+
|
73
|
+
:return: list of ChemStation's current status
|
74
|
+
"""
|
75
|
+
self.send(Command.GET_STATUS_CMD)
|
76
|
+
time.sleep(1)
|
77
|
+
|
78
|
+
try:
|
79
|
+
parsed_response = self.receive().value.string_response
|
80
|
+
self._most_recent_hplc_status = str_to_status(parsed_response)
|
81
|
+
return self._most_recent_hplc_status
|
82
|
+
except IOError:
|
83
|
+
return HPLCErrorStatus.NORESPONSE
|
84
|
+
except IndexError:
|
85
|
+
return HPLCErrorStatus.MALFORMED
|
86
|
+
|
87
|
+
def set_status(self):
|
88
|
+
"""Updates current status of HPLC machine"""
|
89
|
+
self._most_recent_hplc_status = self.get_status()
|
90
|
+
|
91
|
+
def check_if_running(self) -> bool:
|
92
|
+
"""Checks if HPLC machine is in an available state, meaning a state that data is not being written.
|
93
|
+
|
94
|
+
:return: whether the HPLC machine is in a safe state to retrieve data back."""
|
95
|
+
self.set_status()
|
96
|
+
hplc_avail = isinstance(self._most_recent_hplc_status, HPLCAvailStatus)
|
97
|
+
time.sleep(30)
|
98
|
+
self.set_status()
|
99
|
+
hplc_actually_avail = isinstance(self._most_recent_hplc_status, HPLCAvailStatus)
|
100
|
+
return hplc_avail and hplc_actually_avail
|
101
|
+
|
102
|
+
def _send(self, cmd: str, cmd_no: int, num_attempts=5) -> None:
|
103
|
+
"""Low-level execution primitive. Sends a command string to HPLC.
|
104
|
+
|
105
|
+
:param cmd: string to be sent to HPLC
|
106
|
+
:param cmd_no: Command number
|
107
|
+
:param num_attempts: Number of attempts to send the command before raising exception.
|
108
|
+
:raises IOError: Could not write to command file.
|
109
|
+
"""
|
110
|
+
err = None
|
111
|
+
for _ in range(num_attempts):
|
112
|
+
time.sleep(1)
|
113
|
+
try:
|
114
|
+
with open(self.cmd_file, "w", encoding="utf8") as cmd_file:
|
115
|
+
cmd_file.write(f"{cmd_no} {cmd}")
|
116
|
+
except IOError as e:
|
117
|
+
err = e
|
118
|
+
continue
|
119
|
+
else:
|
120
|
+
return
|
121
|
+
else:
|
122
|
+
raise IOError(f"Failed to send command #{cmd_no}: {cmd}.") from err
|
123
|
+
|
124
|
+
def _receive(self, cmd_no: int, num_attempts=100) -> Result[str, str]:
|
125
|
+
"""Low-level execution primitive. Recives a response from HPLC.
|
126
|
+
|
127
|
+
:param cmd_no: Command number
|
128
|
+
:param num_attempts: Number of retries to open reply file
|
129
|
+
:raises IOError: Could not read reply file.
|
130
|
+
:return: Potential ChemStation response
|
131
|
+
"""
|
132
|
+
err = None
|
133
|
+
for _ in range(num_attempts):
|
134
|
+
time.sleep(1)
|
135
|
+
|
136
|
+
try:
|
137
|
+
with open(self.reply_file, "r", encoding="utf_16") as reply_file:
|
138
|
+
response = reply_file.read()
|
139
|
+
except OSError as e:
|
140
|
+
err = e
|
141
|
+
continue
|
142
|
+
|
143
|
+
try:
|
144
|
+
first_line = response.splitlines()[0]
|
145
|
+
response_no = int(first_line.split()[0])
|
146
|
+
except IndexError as e:
|
147
|
+
err = e
|
148
|
+
continue
|
149
|
+
|
150
|
+
# check that response corresponds to sent command
|
151
|
+
if response_no == cmd_no:
|
152
|
+
return Ok(response)
|
153
|
+
else:
|
154
|
+
continue
|
155
|
+
else:
|
156
|
+
return Err(f"Failed to receive reply to command #{cmd_no} due to {err}.")
|
157
|
+
|
158
|
+
def sleepy_send(self, cmd: Union[Command, str]):
|
159
|
+
self.send("Sleep 0.1")
|
160
|
+
self.send(cmd)
|
161
|
+
self.send("Sleep 0.1")
|
162
|
+
|
163
|
+
def send(self, cmd: Union[Command, str]):
|
164
|
+
"""Sends a command to Chemstation.
|
165
|
+
|
166
|
+
:param cmd: Command to be sent to HPLC
|
167
|
+
"""
|
168
|
+
if self.cmd_no == self.MAX_CMD_NO:
|
169
|
+
self.reset_cmd_counter()
|
170
|
+
|
171
|
+
cmd_to_send: str = cmd.value if isinstance(cmd, Command) else cmd
|
172
|
+
self.cmd_no += 1
|
173
|
+
self._send(cmd_to_send, self.cmd_no)
|
174
|
+
f = open("out.txt", "a")
|
175
|
+
f.write(cmd_to_send + "\n")
|
176
|
+
f.close()
|
177
|
+
|
178
|
+
def receive(self) -> Result[Response, str]:
|
179
|
+
"""Returns messages received in reply file.
|
180
|
+
|
181
|
+
:return: ChemStation response
|
182
|
+
"""
|
183
|
+
num_response_prefix = "Numerical Responses:"
|
184
|
+
str_response_prefix = "String Responses:"
|
185
|
+
possible_response = self._receive(self.cmd_no)
|
186
|
+
if Ok(possible_response):
|
187
|
+
lines = possible_response.value.splitlines()
|
188
|
+
for line in lines:
|
189
|
+
if str_response_prefix in line and num_response_prefix in line:
|
190
|
+
string_responses_dirty, _, numerical_responses = line.partition(num_response_prefix)
|
191
|
+
_, _, string_responses = string_responses_dirty.partition(str_response_prefix)
|
192
|
+
return Ok(Response(string_response=string_responses.strip(),
|
193
|
+
num_response=float(numerical_responses.strip())))
|
194
|
+
return Err(f"Could not retrieve HPLC response")
|
195
|
+
else:
|
196
|
+
return Err(f"Could not establish response to HPLC: {possible_response}")
|
197
|
+
|
198
|
+
def reset_cmd_counter(self):
|
199
|
+
"""Resets the command counter."""
|
200
|
+
self._send(Command.RESET_COUNTER_CMD.value, cmd_no=self.MAX_CMD_NO + 1)
|
201
|
+
self._receive(cmd_no=self.MAX_CMD_NO + 1)
|
202
|
+
self.cmd_no = 0
|
203
|
+
|
204
|
+
def stop_macro(self):
|
205
|
+
"""Stops Macro execution. Connection will be lost."""
|
206
|
+
self.send(Command.STOP_MACRO_CMD)
|
@@ -11,11 +11,18 @@ Authors: Alexander Hammer, Hessam Mehr, Lucy Hao
|
|
11
11
|
"""
|
12
12
|
import os
|
13
13
|
import time
|
14
|
-
from typing import Optional
|
14
|
+
from typing import Optional, Union
|
15
15
|
|
16
|
-
from result import
|
16
|
+
from result import Err, Ok, Result
|
17
17
|
|
18
|
-
from ...utils.macro import
|
18
|
+
from ...utils.macro import (
|
19
|
+
str_to_status,
|
20
|
+
HPLCAvailStatus,
|
21
|
+
HPLCErrorStatus,
|
22
|
+
Command,
|
23
|
+
Status,
|
24
|
+
Response,
|
25
|
+
)
|
19
26
|
|
20
27
|
|
21
28
|
class CommunicationController:
|
@@ -193,14 +200,14 @@ class CommunicationController:
|
|
193
200
|
str_response_prefix = "String Responses:"
|
194
201
|
possible_response = self._receive(self.cmd_no)
|
195
202
|
if possible_response.is_ok():
|
196
|
-
lines = possible_response.
|
203
|
+
lines = possible_response.ok_value.splitlines()
|
197
204
|
for line in lines:
|
198
205
|
if str_response_prefix in line and num_response_prefix in line:
|
199
206
|
string_responses_dirty, _, numerical_responses = line.partition(num_response_prefix)
|
200
207
|
_, _, string_responses = string_responses_dirty.partition(str_response_prefix)
|
201
208
|
return Ok(Response(string_response=string_responses.strip(),
|
202
209
|
num_response=float(numerical_responses.strip())))
|
203
|
-
return Err(
|
210
|
+
return Err("Could not retrieve HPLC response")
|
204
211
|
else:
|
205
212
|
return Err(f"Could not establish response to HPLC: {possible_response}")
|
206
213
|
|
@@ -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
|
File without changes
|
@@ -1,13 +1,13 @@
|
|
1
1
|
import abc
|
2
|
-
from typing import Union,
|
2
|
+
from typing import List, Union, Dict, Optional
|
3
3
|
|
4
4
|
from result import Result
|
5
5
|
|
6
|
-
from ....analysis.process_report import
|
6
|
+
from ....analysis.process_report import AgilentReport, ReportType
|
7
7
|
from ....control.controllers import CommunicationController
|
8
8
|
from ....control.controllers.tables.table import TableController
|
9
|
-
from ....utils.chromatogram import AgilentChannelChromatogramData
|
10
|
-
from ....utils.table_types import
|
9
|
+
from ....utils.chromatogram import AgilentChannelChromatogramData, AgilentHPLCChromatogram
|
10
|
+
from ....utils.table_types import T, Table
|
11
11
|
|
12
12
|
|
13
13
|
class DeviceController(TableController, abc.ABC):
|
@@ -26,11 +26,14 @@ class DeviceController(TableController, abc.ABC):
|
|
26
26
|
def retrieve_recent_data_files(self):
|
27
27
|
raise NotImplementedError
|
28
28
|
|
29
|
-
def get_data(self) -> Union[List[AgilentChannelChromatogramData], AgilentChannelChromatogramData]:
|
30
|
-
raise NotImplementedError
|
31
|
-
|
32
29
|
def fuzzy_match_most_recent_folder(self, most_recent_folder: T) -> Result[T, str]:
|
33
30
|
raise NotImplementedError
|
34
31
|
|
35
32
|
def get_report(self, report_type: ReportType = ReportType.TXT) -> List[AgilentReport]:
|
36
33
|
raise NotImplementedError
|
34
|
+
|
35
|
+
def get_data_uv(self) -> Union[List[Dict[str, AgilentHPLCChromatogram]], Dict[str, AgilentHPLCChromatogram]]:
|
36
|
+
raise NotImplementedError
|
37
|
+
|
38
|
+
def get_data(self, custom_path: Optional[str] = None) -> Union[List[AgilentChannelChromatogramData], AgilentChannelChromatogramData]:
|
39
|
+
raise NotImplementedError
|
@@ -1,8 +1,19 @@
|
|
1
|
-
from
|
1
|
+
from __future__ import annotations
|
2
|
+
|
2
3
|
from .device import DeviceController
|
3
|
-
from ....
|
4
|
-
from ....utils.
|
5
|
-
|
4
|
+
from ....control.controllers import CommunicationController
|
5
|
+
from ....utils.injector_types import (
|
6
|
+
Draw,
|
7
|
+
Inject,
|
8
|
+
InjectorTable,
|
9
|
+
Mode,
|
10
|
+
Remote,
|
11
|
+
RemoteCommand,
|
12
|
+
SourceType,
|
13
|
+
Wait,
|
14
|
+
)
|
15
|
+
from ....utils.table_types import RegisterFlag, Table
|
16
|
+
from ....utils.tray_types import Tray
|
6
17
|
|
7
18
|
|
8
19
|
class InjectorController(DeviceController):
|
@@ -10,7 +21,7 @@ class InjectorController(DeviceController):
|
|
10
21
|
def __init__(self, controller: CommunicationController, table: Table, offline: bool):
|
11
22
|
super().__init__(controller, table, offline)
|
12
23
|
|
13
|
-
def get_row(self, row: int) ->
|
24
|
+
def get_row(self, row: int) -> None | Remote | Draw | Wait | Inject:
|
14
25
|
def return_tray_loc() -> Tray:
|
15
26
|
pass
|
16
27
|
|
@@ -32,86 +43,9 @@ class InjectorController(DeviceController):
|
|
32
43
|
return Remote(command=RemoteCommand(self.get_text(row, RegisterFlag.REMOTE)),
|
33
44
|
duration=self.get_num(row, RegisterFlag.REMOTE_DUR))
|
34
45
|
|
35
|
-
def load(self) -> InjectorTable:
|
46
|
+
def load(self) -> InjectorTable | None:
|
36
47
|
rows = self.get_num_rows()
|
37
48
|
if rows.is_ok():
|
38
49
|
return InjectorTable(functions=[self.get_row(i) for i in range(int(rows.ok_value.num_response))])
|
39
50
|
|
40
|
-
|
41
|
-
columns_added = set()
|
42
|
-
|
43
|
-
def add_table_val(col_name: RegisterFlag, val: Union[str, int, float]):
|
44
|
-
nonlocal columns_added
|
45
|
-
if True:
|
46
|
-
if isinstance(val, str):
|
47
|
-
self._edit_row_text(col_name=col_name, val=val)
|
48
|
-
else:
|
49
|
-
self._edit_row_num(col_name=col_name, val=val)
|
50
|
-
else:
|
51
|
-
if isinstance(val, str):
|
52
|
-
self.add_new_col_text(col_name=col_name, val=val)
|
53
|
-
else:
|
54
|
-
self.add_new_col_num(col_name=col_name, val=val)
|
55
|
-
columns_added.add(col_name)
|
56
|
-
|
57
|
-
def add_inject(inject: Inject):
|
58
|
-
add_table_val(col_name=RegisterFlag.FUNCTION, val=inject.__class__.__name__)
|
59
|
-
|
60
|
-
def add_draw(draw: Draw):
|
61
|
-
add_table_val(col_name=RegisterFlag.FUNCTION, val=draw.__class__.__name__)
|
62
|
-
add_table_val(col_name=RegisterFlag.DRAW_SPEED, val=SourceType.DEFAULT.value)
|
63
|
-
add_table_val(col_name=RegisterFlag.DRAW_OFFSET, val=SourceType.DEFAULT.value)
|
64
|
-
|
65
|
-
if draw.amount:
|
66
|
-
add_table_val(col_name=RegisterFlag.DRAW_VOLUME, val=Mode.SET.value)
|
67
|
-
add_table_val(col_name=RegisterFlag.DRAW_VOLUME_VALUE, val=draw.amount)
|
68
|
-
else:
|
69
|
-
add_table_val(col_name=RegisterFlag.DRAW_VOLUME, val=Mode.DEFAULT.value)
|
70
|
-
|
71
|
-
if draw.location:
|
72
|
-
add_table_val(col_name=RegisterFlag.DRAW_SOURCE, val=SourceType.LOCATION.value)
|
73
|
-
add_table_val(col_name=RegisterFlag.DRAW_LOCATION, val=draw.location)
|
74
|
-
elif draw.source:
|
75
|
-
add_table_val(col_name=RegisterFlag.DRAW_SOURCE, val=SourceType.SPECIFIC_LOCATION.value)
|
76
|
-
add_table_val(col_name=RegisterFlag.DRAW_LOCATION_UNIT, val=1)
|
77
|
-
add_table_val(col_name=RegisterFlag.DRAW_LOCATION_TRAY, val=1)
|
78
|
-
add_table_val(col_name=RegisterFlag.DRAW_LOCATION_ROW, val=1)
|
79
|
-
add_table_val(col_name=RegisterFlag.DRAW_LOCATION_COLUMN, val=1)
|
80
|
-
else:
|
81
|
-
add_table_val(col_name=RegisterFlag.DRAW_SOURCE, val=SourceType.DEFAULT.value)
|
82
|
-
|
83
|
-
def add_wait(wait: Wait):
|
84
|
-
add_table_val(col_name=RegisterFlag.FUNCTION, val=wait.__class__.__name__)
|
85
|
-
add_table_val(col_name=RegisterFlag.TIME, val=wait.duration)
|
86
|
-
|
87
|
-
def add_remote(remote: Remote):
|
88
|
-
add_table_val(col_name=RegisterFlag.FUNCTION, val=remote.__class__.__name__)
|
89
|
-
add_table_val(col_name=RegisterFlag.REMOTE, val=remote.command.value)
|
90
|
-
add_table_val(col_name=RegisterFlag.REMOTE_DUR, val=remote.duration)
|
91
|
-
|
92
|
-
self.send(Command.SAVE_METHOD_CMD)
|
93
|
-
rows = self.get_num_rows()
|
94
|
-
if rows.is_ok():
|
95
|
-
existing_row_num = rows.value.num_response
|
96
|
-
for i, function in enumerate(injector_table.functions):
|
97
|
-
if (i+1) > existing_row_num:
|
98
|
-
self.add_row()
|
99
|
-
if isinstance(function, Inject):
|
100
|
-
add_inject(function)
|
101
|
-
elif isinstance(function, Draw):
|
102
|
-
add_draw(function)
|
103
|
-
elif isinstance(function, Wait):
|
104
|
-
add_wait(function)
|
105
|
-
elif isinstance(function, Remote):
|
106
|
-
add_remote(function)
|
107
|
-
self.download()
|
108
|
-
self.send(Command.SAVE_METHOD_CMD)
|
109
|
-
self.send(Command.SWITCH_METHOD_CMD)
|
110
|
-
existing_row_num = self.get_num_rows().ok_value.num_response
|
111
|
-
|
112
|
-
def download(self):
|
113
|
-
self.send('Sleep 1')
|
114
|
-
self.sleepy_send("DownloadRCMethod WLS1")
|
115
|
-
self.send('Sleep 1')
|
116
|
-
self.sleepy_send("DownloadLWls 1")
|
117
|
-
self.send('Sleep 1')
|
51
|
+
|
@@ -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
|