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.
Files changed (31) hide show
  1. pychemstation/__init__.py +1 -1
  2. pychemstation/analysis/__init__.py +3 -4
  3. pychemstation/analysis/base_spectrum.py +7 -7
  4. pychemstation/{utils → analysis}/chromatogram.py +4 -4
  5. pychemstation/analysis/process_report.py +106 -70
  6. pychemstation/control/README.md +21 -53
  7. pychemstation/control/__init__.py +3 -2
  8. pychemstation/control/controllers/__init__.py +1 -5
  9. pychemstation/control/controllers/comm.py +20 -11
  10. pychemstation/control/controllers/devices/device.py +22 -12
  11. pychemstation/control/controllers/devices/injector.py +24 -14
  12. pychemstation/control/controllers/tables/method.py +233 -100
  13. pychemstation/control/controllers/tables/ms.py +7 -4
  14. pychemstation/control/controllers/tables/sequence.py +134 -54
  15. pychemstation/control/controllers/tables/table.py +152 -92
  16. pychemstation/control/hplc.py +96 -78
  17. pychemstation/generated/__init__.py +0 -2
  18. pychemstation/generated/pump_method.py +15 -19
  19. pychemstation/utils/macro.py +10 -9
  20. pychemstation/utils/method_types.py +1 -0
  21. pychemstation/utils/num_utils.py +2 -2
  22. pychemstation/utils/parsing.py +0 -11
  23. pychemstation/utils/sequence_types.py +2 -3
  24. pychemstation/utils/spec_utils.py +2 -3
  25. pychemstation/utils/table_types.py +10 -9
  26. pychemstation/utils/tray_types.py +48 -38
  27. {pychemstation-0.10.2.dist-info → pychemstation-0.10.4.dist-info}/METADATA +46 -20
  28. pychemstation-0.10.4.dist-info/RECORD +37 -0
  29. pychemstation-0.10.2.dist-info/RECORD +0 -37
  30. {pychemstation-0.10.2.dist-info → pychemstation-0.10.4.dist-info}/WHEEL +0 -0
  31. {pychemstation-0.10.2.dist-info → pychemstation-0.10.4.dist-info}/licenses/LICENSE +0 -0
pychemstation/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """
2
2
  .. include:: ../README.md
3
- """
3
+ """
@@ -1,5 +1,4 @@
1
- from .base_spectrum import AbstractSpectrum
1
+ from .process_report import CSVProcessor
2
+ from .process_report import TXTProcessor
2
3
 
3
- __all__ = [
4
- 'AbstractSpectrum',
5
- ]
4
+ __all__ = ["CSVProcessor", "TXTProcessor"]
@@ -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
- self,
201
- filename=None,
202
- title=None,
203
- label=None,
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
 
@@ -249,7 +249,7 @@ class AbstractSpectrum(ABC):
249
249
  os.makedirs(path, exist_ok=True)
250
250
  fig.savefig(os.path.join(path, f"{filename}.png"), dpi=150)
251
251
 
252
- def find_peaks(self, threshold=1, min_width=.1, min_dist=None, area=None):
252
+ def find_peaks(self, threshold=1, min_width=0.1, min_dist=None, area=None):
253
253
  """Finds all peaks above the threshold with at least min_width width.
254
254
 
255
255
  Args:
@@ -385,12 +385,12 @@ class AbstractSpectrum(ABC):
385
385
 
386
386
  if rule == "trapz":
387
387
  return integrate.trapz(
388
- self.y[left_idx: right_idx + 1], self.x[left_idx: right_idx + 1]
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: right_idx + 1], self.x[left_idx: right_idx + 1]
393
+ self.y[left_idx : right_idx + 1], self.x[left_idx : right_idx + 1]
394
394
  )
395
395
 
396
396
  else:
@@ -6,8 +6,9 @@ from dataclasses import dataclass
6
6
 
7
7
  import numpy as np
8
8
 
9
- from ..analysis import AbstractSpectrum
10
- from .parsing import CHFile
9
+
10
+ from ..utils.parsing import CHFile
11
+ from ..analysis.base_spectrum import AbstractSpectrum
11
12
 
12
13
  ACQUISITION_PARAMETERS = "acq.txt"
13
14
 
@@ -36,12 +37,11 @@ class AgilentHPLCChromatogram(AbstractSpectrum):
36
37
  }
37
38
 
38
39
  def __init__(self, path=None, autosaving=False):
39
-
40
40
  if path is not None:
41
41
  os.makedirs(path, exist_ok=True)
42
42
  self.path = path
43
43
  else:
44
- self.path = os.path.join(".", "hplc_data")
44
+ self.path = os.path.join("../utils", "hplc_data")
45
45
  os.makedirs(self.path, exist_ok=True)
46
46
 
47
47
  super().__init__(path=path, autosaving=autosaving)
@@ -17,19 +17,19 @@ from aghplctools.ingestion.text import (
17
17
  )
18
18
  from result import Err, Ok, Result
19
19
 
20
- from pychemstation.utils.chromatogram import AgilentHPLCChromatogram
21
- from pychemstation.utils.tray_types import FiftyFourVialPlate, Tray
20
+ from ..analysis.chromatogram import AgilentHPLCChromatogram
21
+ from ..utils.tray_types import FiftyFourVialPlate, Tray
22
22
 
23
23
 
24
24
  @dataclass
25
25
  class AgilentPeak:
26
+ peak_number: Optional[int]
26
27
  retention_time: float
28
+ peak_type: Optional[str]
27
29
  width: float
28
30
  area: float
29
31
  height: float
30
- peak_number: Optional[int]
31
- peak_type: Optional[str]
32
- height_percent: Optional[float]
32
+ area_percent: Optional[float]
33
33
 
34
34
 
35
35
  @dataclass
@@ -73,12 +73,14 @@ class CSVProcessor(ReportProcessor):
73
73
  """
74
74
  Method to parse details from CSV report.
75
75
 
76
- :returns: subset of complete report details, specifically the sample location, solvents in pumps,
76
+ :return: subset of complete report details, specifically the sample location, solvents in pumps,
77
77
  and list of peaks at each wavelength channel.
78
78
  """
79
- labels = os.path.join(self.path, 'REPORT00.CSV')
79
+ labels = os.path.join(self.path, "REPORT00.CSV")
80
80
  if os.path.exists(labels):
81
- df_labels: Dict[int, Dict[int: AnyStr]] = pd.read_csv(labels, encoding="utf-16", header=None).to_dict()
81
+ df_labels: Dict[int, Dict[int:AnyStr]] = pd.read_csv(
82
+ labels, encoding="utf-16", header=None
83
+ ).to_dict()
82
84
  vial_location = []
83
85
  signals = {}
84
86
  solvents = {}
@@ -91,18 +93,28 @@ class CSVProcessor(ReportProcessor):
91
93
  elif val == "Number of Signals":
92
94
  num_signals = int(df_labels[1][pos])
93
95
  for s in range(1, num_signals + 1):
94
- df = pd.read_csv(os.path.join(self.path, f'REPORT0{s}.CSV'),
95
- encoding="utf-16", header=None)
96
+ df = pd.read_csv(
97
+ os.path.join(self.path, f"REPORT0{s}.CSV"),
98
+ encoding="utf-16",
99
+ header=None,
100
+ )
96
101
  peaks = df.apply(lambda row: AgilentPeak(*row), axis=1)
97
- wavelength = df_labels[1][pos + s].partition(",4 Ref=off")[0][-3:]
98
- signals[wavelength] = peaks
102
+ wavelength = df_labels[1][pos + s].partition(",4 Ref=off")[0][
103
+ -3:
104
+ ]
105
+ signals[wavelength] = list(peaks)
99
106
  break
100
107
 
101
- return Ok(AgilentReport(
102
- signals=[Signals(wavelength=w, peaks=s, data=None) for w, s in signals.items()],
103
- vial_location=FiftyFourVialPlate.from_int(int(vial_location)),
104
- solvents=solvents
105
- ))
108
+ return Ok(
109
+ AgilentReport(
110
+ signals=[
111
+ Signals(wavelength=int(w), peaks=s, data=None)
112
+ for w, s in signals.items()
113
+ ],
114
+ vial_location=FiftyFourVialPlate.from_int(int(vial_location)),
115
+ solvents=solvents,
116
+ )
117
+ )
106
118
 
107
119
  return Err("No report found")
108
120
 
@@ -111,34 +123,39 @@ class TXTProcessor(ReportProcessor):
111
123
  """
112
124
  Regex matches for column and unit combinations, courtesy of Veronica Lai.
113
125
  """
126
+
114
127
  _column_re_dictionary = {
115
- 'Peak': { # peak index
116
- '#': '[ ]+(?P<Peak>[\d]+)', # number
128
+ "Peak": { # peak index
129
+ "#": "[ ]+(?P<Peak>[\d]+)", # number
117
130
  },
118
- 'RetTime': { # retention time
119
- '[min]': '(?P<RetTime>[\d]+.[\d]+)', # minutes
131
+ "RetTime": { # retention time
132
+ "[min]": "(?P<RetTime>[\d]+.[\d]+)", # minutes
120
133
  },
121
- 'Type': { # peak type
122
- '': '(?P<Type>[A-Z]{1,3}(?: [A-Z]{1,2})*)', # todo this is different from <4.8.8 aghplc tools
134
+ "Type": { # peak type
135
+ "": "(?P<Type>[A-Z]{1,3}(?: [A-Z]{1,2})*)", # todo this is different from <4.8.8 aghplc tools
123
136
  },
124
- 'Width': { # peak width
125
- '[min]': '(?P<Width>[\d]+.[\d]+[e+-]*[\d]+)',
137
+ "Width": { # peak width
138
+ "[min]": "(?P<Width>[\d]+.[\d]+[e+-]*[\d]+)",
126
139
  },
127
- 'Area': { # peak area
128
- '[mAU*s]': '(?P<Area>[\d]+.[\d]+[e+-]*[\d]+)', # area units
129
- '%': '(?P<percent>[\d]+.[\d]+[e+-]*[\d]+)', # percent
140
+ "Area": { # peak area
141
+ "[mAU*s]": "(?P<Area>[\d]+.[\d]+[e+-]*[\d]+)", # area units
142
+ "%": "(?P<percent>[\d]+.[\d]+[e+-]*[\d]+)", # percent
130
143
  },
131
- 'Height': { # peak height
132
- '[mAU]': '(?P<Height>[\d]+.[\d]+[e+-]*[\d]+)',
144
+ "Height": { # peak height
145
+ "[mAU]": "(?P<Height>[\d]+.[\d]+[e+-]*[\d]+)",
133
146
  },
134
- 'Name': {
135
- '': '(?P<Name>[^\s]+(?:\s[^\s]+)*)', # peak name
147
+ "Name": {
148
+ "": "(?P<Name>[^\s]+(?:\s[^\s]+)*)", # peak name
136
149
  },
137
150
  }
138
151
 
139
- def __init__(self, path: str, min_ret_time: int = 0,
140
- max_ret_time: int = 999,
141
- target_wavelength_range: List[int] = range(200, 300)):
152
+ def __init__(
153
+ self,
154
+ path: str,
155
+ min_ret_time: int = 0,
156
+ max_ret_time: int = 999,
157
+ target_wavelength_range: List[int] = range(200, 300),
158
+ ):
142
159
  """
143
160
  Class to process reports in CSV form.
144
161
 
@@ -155,16 +172,17 @@ class TXTProcessor(ReportProcessor):
155
172
  def process_report(self) -> Result[AgilentReport, AnyStr]:
156
173
  """
157
174
  Method to parse details from CSV report.
158
-
159
- :returns: subset of complete report details, specifically the sample location, solvents in pumps,
160
- and list of peaks at each wavelength channel.
161
-
162
175
  If you want more functionality, use `aghplctools`.
163
176
  `from aghplctools.ingestion.text import pull_hplc_area_from_txt`
164
177
  `signals = pull_hplc_area_from_txt(file_path)`
178
+
179
+ :return: subset of complete report details, specifically the sample location, solvents in pumps,
180
+ and list of peaks at each wavelength channel.
165
181
  """
166
182
 
167
- with open(os.path.join(self.path, "REPORT.TXT"), 'r', encoding='utf-16') as openfile:
183
+ with open(
184
+ os.path.join(self.path, "REPORT.TXT"), "r", encoding="utf-16"
185
+ ) as openfile:
168
186
  text = openfile.read()
169
187
 
170
188
  try:
@@ -172,25 +190,33 @@ class TXTProcessor(ReportProcessor):
172
190
  except ValueError as e:
173
191
  return Err("No peaks found: " + str(e))
174
192
 
175
- signals = {key: signals[key] for key in self.target_wavelength_range if key in signals}
193
+ signals = {
194
+ key: signals[key] for key in self.target_wavelength_range if key in signals
195
+ }
176
196
 
177
197
  parsed_signals = []
178
198
  for wavelength, wavelength_dict in signals.items():
179
- current_wavelength_signals = Signals(wavelength=wavelength, peaks=[], data=None)
199
+ current_wavelength_signals = Signals(
200
+ wavelength=int(wavelength), peaks=[], data=None
201
+ )
180
202
  for ret_time, ret_time_dict in wavelength_dict.items():
181
203
  if self.min_ret_time <= ret_time <= self.max_ret_time:
182
- current_wavelength_signals.peaks.append(AgilentPeak(retention_time=ret_time,
183
- area=ret_time_dict['Area'],
184
- width=ret_time_dict['Width'],
185
- height=ret_time_dict['Height'],
186
- peak_number=None,
187
- peak_type=ret_time_dict['Type'],
188
- height_percent=None))
204
+ current_wavelength_signals.peaks.append(
205
+ AgilentPeak(
206
+ retention_time=ret_time,
207
+ area=ret_time_dict["Area"],
208
+ width=ret_time_dict["Width"],
209
+ height=ret_time_dict["Height"],
210
+ peak_number=None,
211
+ peak_type=ret_time_dict["Type"],
212
+ area_percent=None,
213
+ )
214
+ )
189
215
  parsed_signals.append(current_wavelength_signals)
190
216
 
191
- return Ok(AgilentReport(vial_location=None,
192
- solvents=None,
193
- signals=parsed_signals))
217
+ return Ok(
218
+ AgilentReport(vial_location=None, solvents=None, signals=parsed_signals)
219
+ )
194
220
 
195
221
  def parse_area_report(self, report_text: str) -> Dict:
196
222
  """
@@ -206,7 +232,7 @@ class TXTProcessor(ReportProcessor):
206
232
  should be able to use the `parse_area_report` method of aghplctools v4.8.8
207
233
  """
208
234
  if re.search(_no_peaks_re, report_text): # There are no peaks in Report.txt
209
- raise ValueError('No peaks found in Report.txt')
235
+ raise ValueError("No peaks found in Report.txt")
210
236
  blocks = _header_block_re.split(report_text)
211
237
  signals = {} # output dictionary
212
238
  for ind, block in enumerate(blocks):
@@ -219,23 +245,28 @@ class TXTProcessor(ReportProcessor):
219
245
  si = _signal_info_re.match(table)
220
246
  if si is not None:
221
247
  # some error state (e.g. 'not found')
222
- if si.group('error') != '':
248
+ if si.group("error") != "":
223
249
  continue
224
- wavelength = float(si.group('wavelength'))
250
+ wavelength = float(si.group("wavelength"))
225
251
  if wavelength in signals:
226
252
  # placeholder error raise just in case (this probably won't happen)
227
253
  raise KeyError(
228
- f'The wavelength {float(si.group("wavelength"))} is already in the signals dictionary')
254
+ f"The wavelength {float(si.group('wavelength'))} is already in the signals dictionary"
255
+ )
229
256
  signals[wavelength] = {}
230
257
  # build peak regex
231
258
  peak_re = self.build_peak_regex(table)
232
- if peak_re is None: # if there are no columns (empty table), continue
259
+ if (
260
+ peak_re is None
261
+ ): # if there are no columns (empty table), continue
233
262
  continue
234
- for line in table.split('\n'):
263
+ for line in table.split("\n"):
235
264
  peak = peak_re.match(line)
236
265
  if peak is not None:
237
- signals[wavelength][float(peak.group('RetTime'))] = {}
238
- current = signals[wavelength][float(peak.group('RetTime'))]
266
+ signals[wavelength][float(peak.group("RetTime"))] = {}
267
+ current = signals[wavelength][
268
+ float(peak.group("RetTime"))
269
+ ]
239
270
  for key in self._column_re_dictionary:
240
271
  if key in peak.re.groupindex:
241
272
  try: # try float conversion, otherwise continue
@@ -254,30 +285,35 @@ class TXTProcessor(ReportProcessor):
254
285
  :param signal_table: block of lines associated with an area table
255
286
  :return: peak line regex object (<=3.6 _sre.SRE_PATTERN, >=3.7 re.Pattern)
256
287
  """
257
- split_table = signal_table.split('\n')
288
+ split_table = signal_table.split("\n")
258
289
  if len(split_table) <= 4: # catch peak table with no values
259
290
  return None
260
291
  # todo verify that these indicies are always true
261
292
  column_line = split_table[2] # table column line
262
293
  unit_line = split_table[3] # column unit line
263
- length_line = [len(val) + 1 for val in split_table[4].split('|')] # length line
294
+ length_line = [len(val) + 1 for val in split_table[4].split("|")] # length line
264
295
 
265
296
  # iterate over header values and units to build peak table regex
266
297
  peak_re_string = []
267
298
  for header, unit in zip(
268
- chunk_string(column_line, length_line),
269
- chunk_string(unit_line, length_line)
299
+ chunk_string(column_line, length_line), chunk_string(unit_line, length_line)
270
300
  ):
271
- if header == '': # todo create a better catch for an undefined header
301
+ if header == "": # todo create a better catch for an undefined header
272
302
  continue
273
303
  try:
274
304
  peak_re_string.append(
275
- self._column_re_dictionary[header][unit] # append the appropriate regex
305
+ self._column_re_dictionary[header][
306
+ unit
307
+ ] # append the appropriate regex
276
308
  )
277
309
  except KeyError: # catch for undefined regexes (need to be built)
278
- raise KeyError(f'The header/unit combination "{header}" "{unit}" is not defined in the peak regex '
279
- f'dictionary. Let Lars know.')
310
+ raise KeyError(
311
+ f'The header/unit combination "{header}" "{unit}" is not defined in the peak regex '
312
+ f"dictionary. Let Lars know."
313
+ )
280
314
  return re.compile(
281
- '[ ]+'.join(peak_re_string) # constructed string delimited by 1 or more spaces
282
- + '[\s]*' # and any remaining white space
315
+ "[ ]+".join(
316
+ peak_re_string
317
+ ) # constructed string delimited by 1 or more spaces
318
+ + "[\s]*" # and any remaining white space
283
319
  )
@@ -1,30 +1,24 @@
1
1
  # Examples of usecases
2
2
 
3
- ## Initialization
4
-
5
3
  ```python
6
4
  from pychemstation.control import HPLCController
7
5
 
8
6
  DEFAULT_METHOD_DIR = "C:\\ChemStation\\1\\Methods\\"
9
- DATA_DIR = "C:\\Users\\Public\\Documents\\ChemStation\\3\\Data"
10
7
  SEQUENCE_DIR = "C:\\USERS\\PUBLIC\\DOCUMENTS\\CHEMSTATION\\3\\Sequence"
11
8
  DEFAULT_COMMAND_PATH = "C:\\Users\\User\\Desktop\\Lucy\\"
9
+ DATA_DIR_2 = "C:\\Users\\Public\\Documents\\ChemStation\\2\\Data"
10
+ DATA_DIR_3 = "C:\\Users\\Public\\Documents\\ChemStation\\3\\Data"
12
11
 
13
- hplc_controller = HPLCController(data_dir=DATA_DIR,
12
+ # Initialize HPLC Controller
13
+ hplc_controller = HPLCController(data_dirs=[DATA_DIR_2, DATA_DIR_3],
14
14
  comm_dir=DEFAULT_COMMAND_PATH,
15
15
  method_dir=DEFAULT_METHOD_DIR,
16
16
  sequence_dir=SEQUENCE_DIR)
17
- ```
18
-
19
- ## Switching a method
20
17
 
21
- ```python
18
+ # Switching a method
22
19
  hplc_controller.switch_method("General-Poroshell")
23
- ```
24
20
 
25
- ## Editing a method
26
-
27
- ```python
21
+ # Editing a method
28
22
  from pychemstation.utils.method_types import *
29
23
 
30
24
  new_method = MethodDetails(
@@ -47,52 +41,27 @@ new_method = MethodDetails(
47
41
  stop_time=5,
48
42
  post_time=2
49
43
  )
50
-
51
44
  hplc_controller.edit_method(new_method)
52
- ```
53
-
54
- ## Running a method and get data from last run method
55
45
 
56
- ```python
46
+ # Run a method and get a report or data from last run method
57
47
  hplc_controller.run_method(experiment_name="test_experiment")
58
48
  chrom = hplc_controller.get_last_run_method_data()
59
49
  channel_a_time = chrom.A.x
60
- ```
50
+ report = hplc_controller.get_last_run_method_report()
51
+ vial_location = report.vial_location
61
52
 
62
- ## Switching a sequence
63
-
64
- ```python
53
+ # switch the currently loaded sequence
65
54
  hplc_controller.switch_sequence(sequence_name="hplc_testing")
66
- ```
67
55
 
68
- ## Editing a Sequence Row
69
-
70
- ```python
71
- from pychemstation.utils.sequence_types import *
72
- from pychemstation.utils.tray_types import *
73
-
74
- hplc_controller.edit_sequence_row(SequenceEntry(
75
- vial_location=FiftyFourVialPlate(plate=Plate.TWO, letter=Letter.A, num=Num.SEVEN).value(),
76
- method="General-Poroshell",
77
- num_inj=3,
78
- inj_vol=4,
79
- sample_name="Blank",
80
- sample_type=SampleType.BLANK,
81
- inj_source=InjectionSource.HIP_ALS
82
- ), 1)
83
- ```
84
-
85
- ## Editing entire Sequence Table
86
-
87
- ```python
56
+ # edit the sequence table
88
57
  from pychemstation.utils.tray_types import *
89
58
  from pychemstation.utils.sequence_types import *
90
59
 
91
60
  seq_table = SequenceTable(
92
- name=DEFAULT_SEQUENCE,
61
+ name="hplc_testing",
93
62
  rows=[
94
63
  SequenceEntry(
95
- vial_location=FiftyFourVialPlate(plate=Plate.TWO, letter=Letter.A, num=Num.SEVEN).value(),
64
+ vial_location=FiftyFourVialPlate.from_str("P1-A1"),
96
65
  method="General-Poroshell",
97
66
  num_inj=3,
98
67
  inj_vol=4,
@@ -101,7 +70,7 @@ seq_table = SequenceTable(
101
70
  inj_source=InjectionSource.MANUAL
102
71
  ),
103
72
  SequenceEntry(
104
- vial_location=TenVialColumn.ONE.value,
73
+ vial_location=TenVialColumn.ONE,
105
74
  method="General-Poroshell",
106
75
  num_inj=1,
107
76
  inj_vol=1,
@@ -110,7 +79,7 @@ seq_table = SequenceTable(
110
79
  inj_source=InjectionSource.AS_METHOD
111
80
  ),
112
81
  SequenceEntry(
113
- vial_location=10,
82
+ vial_location=FiftyFourVialPlate.from_str("P2-B4"),
114
83
  method="General-Poroshell",
115
84
  num_inj=3,
116
85
  inj_vol=4,
@@ -121,12 +90,11 @@ seq_table = SequenceTable(
121
90
  ]
122
91
  )
123
92
  hplc_controller.edit_sequence(seq_table)
124
- ```
125
93
 
126
- ## Running a sequence and get data from last run sequence
127
-
128
- ```python
129
- hplc_controller.run_sequence(seq_table)
130
- chroms = hplc_controller.get_last_run_sequence_data()
131
- channel_A_time = chroms[0].A.x
94
+ # Run a sequence and get data or report from last run sequence
95
+ hplc_controller.run_sequence()
96
+ chroms = hplc_controller.get_last_run_sequence_data(read_uv=True)
97
+ row_1_channel_A_abs = chroms[0][210].y
98
+ report = hplc_controller.get_last_run_sequence_reports()
99
+ vial_location_row_1 = report[0].vial_location
132
100
  ```
@@ -1,8 +1,9 @@
1
1
  """
2
2
  .. include:: README.md
3
3
  """
4
+
4
5
  from .hplc import HPLCController
5
6
 
6
7
  __all__ = [
7
- 'HPLCController',
8
- ]
8
+ "HPLCController",
9
+ ]
@@ -6,8 +6,4 @@ from .comm import CommunicationController
6
6
  from .tables.method import MethodController
7
7
  from .tables.sequence import SequenceController
8
8
 
9
- __all__ = [
10
- 'CommunicationController',
11
- 'MethodController',
12
- 'SequenceController'
13
- ]
9
+ __all__ = ["CommunicationController", "MethodController", "SequenceController"]
@@ -9,6 +9,7 @@ been processed.
9
9
 
10
10
  Authors: Alexander Hammer, Hessam Mehr, Lucy Hao
11
11
  """
12
+
12
13
  import os
13
14
  import time
14
15
  from typing import Optional, Union
@@ -34,11 +35,11 @@ class CommunicationController:
34
35
  MAX_CMD_NO = 255
35
36
 
36
37
  def __init__(
37
- self,
38
- comm_dir: str,
39
- cmd_file: str = "cmd",
40
- reply_file: str = "reply",
41
- debug: bool = False
38
+ self,
39
+ comm_dir: str,
40
+ cmd_file: str = "cmd",
41
+ reply_file: str = "reply",
42
+ debug: bool = False,
42
43
  ):
43
44
  """
44
45
  :param comm_dir:
@@ -62,7 +63,7 @@ class CommunicationController:
62
63
  self.reset_cmd_counter()
63
64
 
64
65
  # Initialize row counter for table operations
65
- self.send('Local Rows')
66
+ self.send("Local Rows")
66
67
 
67
68
  def get_num_val(self, cmd: str) -> Union[int, float]:
68
69
  tries = 5
@@ -194,7 +195,7 @@ class CommunicationController:
194
195
  def receive(self) -> Result[Response, str]:
195
196
  """Returns messages received in reply file.
196
197
 
197
- :return: ChemStation response
198
+ :return: ChemStation response
198
199
  """
199
200
  num_response_prefix = "Numerical Responses:"
200
201
  str_response_prefix = "String Responses:"
@@ -203,10 +204,18 @@ class CommunicationController:
203
204
  lines = possible_response.ok_value.splitlines()
204
205
  for line in lines:
205
206
  if str_response_prefix in line and num_response_prefix in line:
206
- string_responses_dirty, _, numerical_responses = line.partition(num_response_prefix)
207
- _, _, string_responses = string_responses_dirty.partition(str_response_prefix)
208
- return Ok(Response(string_response=string_responses.strip(),
209
- num_response=float(numerical_responses.strip())))
207
+ string_responses_dirty, _, numerical_responses = line.partition(
208
+ num_response_prefix
209
+ )
210
+ _, _, string_responses = string_responses_dirty.partition(
211
+ str_response_prefix
212
+ )
213
+ return Ok(
214
+ Response(
215
+ string_response=string_responses.strip(),
216
+ num_response=float(numerical_responses.strip()),
217
+ )
218
+ )
210
219
  return Err("Could not retrieve HPLC response")
211
220
  else:
212
221
  return Err(f"Could not establish response to HPLC: {possible_response}")
@@ -6,18 +6,20 @@ from result import Result
6
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, AgilentHPLCChromatogram
9
+ from pychemstation.analysis.chromatogram import (
10
+ AgilentChannelChromatogramData,
11
+ AgilentHPLCChromatogram,
12
+ )
10
13
  from ....utils.table_types import T, Table
11
14
 
12
15
 
13
16
  class DeviceController(TableController, abc.ABC):
14
-
15
- def __init__(self, controller: CommunicationController, table: Table, offline: bool):
16
- super().__init__(controller=controller,
17
- src=None,
18
- data_dirs=[],
19
- table=table,
20
- offline=offline)
17
+ def __init__(
18
+ self, controller: CommunicationController, table: Table, offline: bool
19
+ ):
20
+ super().__init__(
21
+ controller=controller, src=None, data_dirs=[], table=table, offline=offline
22
+ )
21
23
 
22
24
  @abc.abstractmethod
23
25
  def get_row(self, row: int):
@@ -29,11 +31,19 @@ class DeviceController(TableController, abc.ABC):
29
31
  def fuzzy_match_most_recent_folder(self, most_recent_folder: T) -> Result[T, str]:
30
32
  raise NotImplementedError
31
33
 
32
- def get_report(self, report_type: ReportType = ReportType.TXT) -> List[AgilentReport]:
34
+ def get_report(
35
+ self, report_type: ReportType = ReportType.TXT
36
+ ) -> List[AgilentReport]:
33
37
  raise NotImplementedError
34
38
 
35
- def get_data_uv(self) -> Union[List[Dict[str, AgilentHPLCChromatogram]], Dict[str, AgilentHPLCChromatogram]]:
39
+ def get_data_uv(
40
+ self,
41
+ ) -> Union[
42
+ List[Dict[str, AgilentHPLCChromatogram]], Dict[str, AgilentHPLCChromatogram]
43
+ ]:
36
44
  raise NotImplementedError
37
45
 
38
- def get_data(self, custom_path: Optional[str] = None) -> Union[List[AgilentChannelChromatogramData], AgilentChannelChromatogramData]:
39
- raise NotImplementedError
46
+ def get_data(
47
+ self, custom_path: Optional[str] = None
48
+ ) -> Union[List[AgilentChannelChromatogramData], AgilentChannelChromatogramData]:
49
+ raise NotImplementedError