pychemstation 0.8.3__py3-none-any.whl → 0.10.1__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.
@@ -1 +1,5 @@
1
1
  from .base_spectrum import AbstractSpectrum
2
+
3
+ __all__ = [
4
+ 'AbstractSpectrum',
5
+ ]
@@ -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 .utils import interpolate_to_index, find_nearest_value_index
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
- 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
 
@@ -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:
@@ -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 List, AnyStr, Dict, Optional, Pattern
7
+ from typing import AnyStr, Dict, List, Optional, Pattern
8
8
 
9
9
  import pandas as pd
10
- from aghplctools.ingestion.text import _no_peaks_re, _area_report_re, _header_block_re, _signal_info_re, \
11
- _signal_table_re, chunk_string
12
- from result import Result, Err, Ok
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 Tray, FiftyFourVialPlate
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, f'REPORT00.CSV')
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(f'No peaks found in Report.txt')
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):
@@ -1,6 +1,7 @@
1
- # Examples of usecases
1
+ # Examples of usecases
2
2
 
3
3
  ## Initialization
4
+
4
5
  ```python
5
6
  from pychemstation.control import HPLCController
6
7
 
@@ -16,6 +17,7 @@ hplc_controller = HPLCController(data_dir=DATA_DIR,
16
17
  ```
17
18
 
18
19
  ## Switching a method
20
+
19
21
  ```python
20
22
  hplc_controller.switch_method("General-Poroshell")
21
23
  ```
@@ -50,6 +52,7 @@ hplc_controller.edit_method(new_method)
50
52
  ```
51
53
 
52
54
  ## Running a method and get data from last run method
55
+
53
56
  ```python
54
57
  hplc_controller.run_method(experiment_name="test_experiment")
55
58
  chrom = hplc_controller.get_last_run_method_data()
@@ -57,10 +60,13 @@ channel_a_time = chrom.A.x
57
60
  ```
58
61
 
59
62
  ## Switching a sequence
63
+
60
64
  ```python
61
65
  hplc_controller.switch_sequence(sequence_name="hplc_testing")
62
66
  ```
67
+
63
68
  ## Editing a Sequence Row
69
+
64
70
  ```python
65
71
  from pychemstation.utils.sequence_types import *
66
72
  from pychemstation.utils.tray_types import *
@@ -77,6 +83,7 @@ hplc_controller.edit_sequence_row(SequenceEntry(
77
83
  ```
78
84
 
79
85
  ## Editing entire Sequence Table
86
+
80
87
  ```python
81
88
  from pychemstation.utils.tray_types import *
82
89
  from pychemstation.utils.sequence_types import *
@@ -117,6 +124,7 @@ hplc_controller.edit_sequence(seq_table)
117
124
  ```
118
125
 
119
126
  ## Running a sequence and get data from last run sequence
127
+
120
128
  ```python
121
129
  hplc_controller.run_sequence(seq_table)
122
130
  chroms = hplc_controller.get_last_run_sequence_data()
@@ -2,3 +2,7 @@
2
2
  .. include:: README.md
3
3
  """
4
4
  from .hplc import HPLCController
5
+
6
+ __all__ = [
7
+ 'HPLCController',
8
+ ]
@@ -5,3 +5,9 @@
5
5
  from .comm import CommunicationController
6
6
  from .tables.method import MethodController
7
7
  from .tables.sequence import SequenceController
8
+
9
+ __all__ = [
10
+ 'CommunicationController',
11
+ 'MethodController',
12
+ 'SequenceController'
13
+ ]
@@ -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 Result, Ok, Err
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.value.splitlines()
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(f"Could not retrieve HPLC response")
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
 
@@ -1,13 +1,13 @@
1
1
  import abc
2
- from typing import Union, List
2
+ from typing import List, Union, Dict, Optional
3
3
 
4
4
  from result import Result
5
5
 
6
- from ....analysis.process_report import ReportType, AgilentReport
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 Table, T
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 ....control.controllers import CommunicationController
1
+ from __future__ import annotations
2
+
2
3
  from .device import DeviceController
3
- from ....utils.injector_types import *
4
- from ....utils.macro import Command
5
- from ....utils.table_types import Table, RegisterFlag
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) -> InjectorFunction:
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
- def edit(self, injector_table: InjectorTable):
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
+
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  import time
3
- from typing import List
3
+ import warnings
4
+ from typing import Dict, List, Optional, Union
4
5
 
5
6
  from result import Err, Ok, Result
6
7
  from xsdata.formats.dataclass.parsers import XmlParser
@@ -13,9 +14,15 @@ from ....utils.chromatogram import (
13
14
  AgilentChannelChromatogramData,
14
15
  AgilentHPLCChromatogram,
15
16
  )
16
- from ....utils.macro import *
17
- from ....utils.method_types import *
18
- from ....utils.table_types import *
17
+ from ....utils.macro import Command
18
+ from ....utils.method_types import (
19
+ HPLCMethodParams,
20
+ MethodDetails,
21
+ Param,
22
+ PType,
23
+ TimeTableEntry,
24
+ )
25
+ from ....utils.table_types import RegisterFlag, T, Table, TableOperation
19
26
  from ..devices.injector import InjectorController
20
27
  from .table import TableController
21
28
 
@@ -129,6 +136,10 @@ class MethodController(TableController):
129
136
  register=self.table_locator.register,
130
137
  register_flag=RegisterFlag.MAX_TIME))
131
138
 
139
+ def get_total_runtime(self) -> Union[int, float]:
140
+ """Returns total method runtime in minutes."""
141
+ return self.get_post_time() + self.get_stop_time()
142
+
132
143
  def current_method(self, method_name: str):
133
144
  """
134
145
  Checks if a given method is already loaded into Chemstation. Method name does not need the ".M" extension.
@@ -173,6 +184,7 @@ class MethodController(TableController):
173
184
  :raises FileNotFoundError: Method does not exist
174
185
  :return: method details
175
186
  """
187
+ warnings.warn("This method is not actively maintained.")
176
188
  method_folder = f"{method_name}.M"
177
189
  method_path = os.path.join(self.src, method_folder, "AgilentPumpDriver1.RapidControl.MethodXML.xml")
178
190
  dad_path = os.path.join(self.src, method_folder, "Agilent1200erDadDriver1.RapidControl.MethodXML.xml")
@@ -347,29 +359,39 @@ class MethodController(TableController):
347
359
  raise RuntimeError("Method failed to start.")
348
360
 
349
361
  self.data_files.append(os.path.join(self.data_dirs[0], folder_name))
362
+ self.timeout = (self.get_total_runtime()) * 60
350
363
 
351
364
  if stall_while_running:
352
- self.timeout = (self.get_stop_time() + self.get_post_time()) * 60
353
365
  run_completed = self.check_hplc_done_running()
354
366
  if run_completed.is_ok():
355
367
  self.data_files[-1] = run_completed.ok_value
356
368
  else:
357
369
  raise RuntimeError("Run error has occurred.")
358
370
  else:
359
- self.data_files[-1] = self.fuzzy_match_most_recent_folder(folder_name).ok_value
371
+ folder = self.fuzzy_match_most_recent_folder(self.data_files[-1])
372
+ while folder.is_err():
373
+ folder = self.fuzzy_match_most_recent_folder(self.data_files[-1])
374
+ if folder.is_ok():
375
+ self.data_files[-1] = folder.ok_value
376
+ else:
377
+ warning = f"Data folder {self.data_files[-1]} may not exist, returning and will check again after run is done."
378
+ warnings.warn(warning)
379
+
360
380
 
361
381
  def fuzzy_match_most_recent_folder(self, most_recent_folder: T) -> Result[T, str]:
362
382
  if os.path.exists(most_recent_folder):
363
383
  return Ok(most_recent_folder)
364
384
  return Err("Folder not found!")
365
385
 
366
- def get_data(self, custom_path: Optional[str] = None,
367
- read_uv: bool = False) -> AgilentChannelChromatogramData:
368
- if not custom_path:
369
- self.get_spectrum(self.data_files[-1], read_uv)
370
- else:
371
- self.get_spectrum(custom_path, read_uv)
372
- return AgilentChannelChromatogramData(**self.spectra) if not read_uv else self.uv
386
+ def get_data(self, custom_path: Optional[str] = None) -> AgilentChannelChromatogramData:
387
+ custom_path = custom_path if custom_path else self.data_files[-1]
388
+ self.get_spectrum_at_channels(custom_path)
389
+ return AgilentChannelChromatogramData(**self.spectra)
390
+
391
+ def get_data_uv(self, custom_path: Optional[str] = None) -> Dict[str, AgilentHPLCChromatogram]:
392
+ custom_path = custom_path if custom_path else self.data_files[-1]
393
+ self.get_uv_spectrum(custom_path)
394
+ return self.uv
373
395
 
374
396
  def get_report(self, custom_path: Optional[str] = None,
375
397
  report_type: ReportType = ReportType.TXT) -> List[AgilentReport]:
@@ -1,19 +1,29 @@
1
1
  import os
2
2
  import time
3
- from typing import Optional, List, Dict
3
+ from typing import Dict, List, Optional
4
4
 
5
- from result import Result, Ok, Err
5
+ from result import Err, Ok, Result
6
6
  from typing_extensions import override
7
7
 
8
- from .table import TableController
9
- from .. import MethodController
10
- from ....analysis.process_report import ReportType, AgilentReport
8
+ from ....analysis.process_report import AgilentReport, ReportType
11
9
  from ....control.controllers.comm import CommunicationController
12
- from ....utils.chromatogram import SEQUENCE_TIME_FORMAT, AgilentChannelChromatogramData, AgilentHPLCChromatogram
10
+ from ....utils.chromatogram import (
11
+ SEQUENCE_TIME_FORMAT,
12
+ AgilentChannelChromatogramData,
13
+ AgilentHPLCChromatogram,
14
+ )
13
15
  from ....utils.macro import Command
14
- from ....utils.sequence_types import SequenceTable, SequenceEntry, SequenceDataFiles, InjectionSource, SampleType
16
+ from ....utils.sequence_types import (
17
+ InjectionSource,
18
+ SampleType,
19
+ SequenceDataFiles,
20
+ SequenceEntry,
21
+ SequenceTable,
22
+ )
15
23
  from ....utils.table_types import RegisterFlag, Table
16
- from ....utils.tray_types import TenVialColumn, FiftyFourVialPlate
24
+ from ....utils.tray_types import FiftyFourVialPlate, TenVialColumn
25
+ from .. import MethodController
26
+ from .table import TableController
17
27
 
18
28
 
19
29
  class SequenceController(TableController):
@@ -82,7 +92,7 @@ class SequenceController(TableController):
82
92
  time.sleep(2)
83
93
  self.send(Command.GET_SEQUENCE_CMD)
84
94
  time.sleep(2)
85
- parsed_response = self.receive().value.string_response
95
+ parsed_response = self.receive().ok_value.string_response
86
96
 
87
97
  assert parsed_response == f"{seq_name}.S", "Switching sequence failed."
88
98
  self.table_state = None
@@ -97,7 +107,7 @@ class SequenceController(TableController):
97
107
  self.table_state = sequence_table
98
108
  rows = self.get_num_rows()
99
109
  if rows.is_ok():
100
- existing_row_num = rows.value.num_response
110
+ existing_row_num = rows.ok_value.num_response
101
111
  wanted_row_num = len(sequence_table.rows)
102
112
  while existing_row_num != wanted_row_num:
103
113
  if wanted_row_num > existing_row_num:
@@ -127,7 +137,6 @@ class SequenceController(TableController):
127
137
  self.add_row()
128
138
  self.send(Command.SAVE_SEQUENCE_CMD)
129
139
  num_rows = self.get_num_rows()
130
-
131
140
  if row.vial_location:
132
141
  loc = row.vial_location
133
142
  if isinstance(loc, TenVialColumn):
@@ -135,7 +144,6 @@ class SequenceController(TableController):
135
144
  elif isinstance(loc, FiftyFourVialPlate):
136
145
  loc = row.vial_location.value()
137
146
  self._edit_row_num(row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc)
138
-
139
147
  if row.method:
140
148
  method_dir = self.method_controller.src
141
149
  possible_path = os.path.join(method_dir, row.method) + ".M\\"
@@ -143,23 +151,18 @@ class SequenceController(TableController):
143
151
  if os.path.exists(possible_path):
144
152
  method = os.path.join(method_dir, row.method)
145
153
  self._edit_row_text(row=row_num, col_name=RegisterFlag.METHOD, val=method)
146
-
147
154
  if row.num_inj:
148
155
  self._edit_row_num(row=row_num, col_name=RegisterFlag.NUM_INJ, val=row.num_inj)
149
-
150
156
  if row.inj_vol:
151
157
  self._edit_row_text(row=row_num, col_name=RegisterFlag.INJ_VOL, val=row.inj_vol)
152
-
153
158
  if row.inj_source:
154
159
  self._edit_row_text(row=row_num, col_name=RegisterFlag.INJ_SOR, val=row.inj_source.value)
155
-
156
160
  if row.sample_name:
157
161
  self._edit_row_text(row=row_num, col_name=RegisterFlag.NAME, val=row.sample_name)
158
162
  if row.data_file:
159
163
  self._edit_row_text(row=row_num, col_name=RegisterFlag.DATA_FILE, val=row.data_file)
160
164
  else:
161
165
  self._edit_row_text(row=row_num, col_name=RegisterFlag.DATA_FILE, val=row.sample_name)
162
-
163
166
  if row.sample_type:
164
167
  self._edit_row_num(row=row_num, col_name=RegisterFlag.SAMPLE_TYPE, val=row.sample_type.value)
165
168
 
@@ -179,15 +182,15 @@ class SequenceController(TableController):
179
182
 
180
183
  total_runtime = 0
181
184
  for entry in self.table_state.rows:
182
- curr_method_runtime = self.method_controller.get_post_time() + self.method_controller.get_stop_time()
183
- loaded_method = self.method_controller.get_method_name()[:-2]
185
+ curr_method_runtime = self.method_controller.get_total_runtime()
186
+ loaded_method = self.method_controller.get_method_name().removesuffix(".M")
184
187
  method_path = entry.method.split(sep="\\")
185
188
  method_name = method_path[-1]
186
189
  if loaded_method != method_name:
187
- method_dir = os.path.join(*method_path[:-1]) if len(method_path) > 1 else None
190
+ method_dir = "\\".join(method_path[:-1])+"\\" if len(method_path) > 1 else None
188
191
  self.method_controller.switch(method_name=method_name,
189
192
  alt_method_dir=method_dir)
190
- curr_method_runtime = self.method_controller.get_post_time() + self.method_controller.get_stop_time()
193
+ curr_method_runtime = self.method_controller.get_total_runtime()
191
194
  total_runtime += curr_method_runtime
192
195
 
193
196
  timestamp = time.strftime(SEQUENCE_TIME_FORMAT)
@@ -196,8 +199,8 @@ class SequenceController(TableController):
196
199
 
197
200
  if self.check_hplc_is_running():
198
201
  folder_name = f"{self.table_state.name} {timestamp}"
199
- self.data_files.append(SequenceDataFiles(dir=folder_name,
200
- sequence_name=self.table_state.name))
202
+ data_file = SequenceDataFiles(dir=folder_name, sequence_name=self.table_state.name)
203
+ self.data_files.append(data_file)
201
204
 
202
205
  if stall_while_running:
203
206
  run_completed = self.check_hplc_done_running()
@@ -205,10 +208,8 @@ class SequenceController(TableController):
205
208
  self.data_files[-1] = run_completed.ok_value
206
209
  else:
207
210
  raise RuntimeError("Run error has occurred.")
208
- else:
209
- self.data_files[-1] = SequenceDataFiles(dir=folder_name,
210
- child_dirs=[],
211
- sequence_name=self.table_state.name)
211
+ else:
212
+ raise RuntimeError("Sequence run did not start.")
212
213
 
213
214
  @override
214
215
  def fuzzy_match_most_recent_folder(self, most_recent_folder: SequenceDataFiles) -> Result[SequenceDataFiles, str]:
@@ -248,17 +249,25 @@ class SequenceController(TableController):
248
249
  except Exception:
249
250
  return Err("Failed to get sequence folder")
250
251
 
251
- def get_data(self, custom_path: Optional[str] = None,
252
- read_uv: bool = False) -> List[AgilentChannelChromatogramData]:
253
- if len(self.data_files[-1].child_dirs) == 0:
254
- self.data_files[-1] = self.fuzzy_match_most_recent_folder(self.data_files[-1]).ok_value
255
- spectra: list[AgilentChannelChromatogramData] = []
256
- all_w_spectra: list[Dict[str, AgilentHPLCChromatogram]] = []
252
+ def get_data_uv(self,custom_path: Optional[str] = None) -> List[Dict[str, AgilentHPLCChromatogram]]:
253
+ custom_path = SequenceDataFiles(dir=custom_path, child_dirs=[], sequence_name="") if custom_path else self.data_files[-1]
254
+ if len(custom_path.child_dirs) == 0:
255
+ self.data_files[-1] = self.fuzzy_match_most_recent_folder(custom_path).ok_value
256
+ all_w_spectra: List[Dict[str, AgilentHPLCChromatogram]] = []
257
257
  for row in self.data_files[-1].child_dirs:
258
- self.get_spectrum(row, read_uv)
259
- spectra.append(AgilentChannelChromatogramData(**self.spectra))
258
+ self.get_uv_spectrum(row)
260
259
  all_w_spectra.append(self.uv)
261
- return spectra if not read_uv else all_w_spectra
260
+ return all_w_spectra
261
+
262
+ def get_data(self, custom_path: Optional[str] = None) -> List[AgilentChannelChromatogramData]:
263
+ custom_path = SequenceDataFiles(dir=custom_path, child_dirs=[], sequence_name="") if custom_path else self.data_files[-1]
264
+ if len(custom_path.child_dirs) == 0:
265
+ self.data_files[-1] = self.fuzzy_match_most_recent_folder(custom_path).ok_value
266
+ spectra: List[AgilentChannelChromatogramData] = []
267
+ for row in self.data_files[-1].child_dirs:
268
+ self.get_spectrum_at_channels(row)
269
+ spectra.append(AgilentChannelChromatogramData(**self.spectra))
270
+ return spectra
262
271
 
263
272
  def get_report(self, custom_path: Optional[str] = None,
264
273
  report_type: ReportType = ReportType.TXT) -> List[AgilentReport]:
@@ -268,7 +277,7 @@ class SequenceController(TableController):
268
277
  child_dirs=[],
269
278
  sequence_name="NA")).ok_value)
270
279
  parent_dir = self.data_files[-1]
271
- spectra = self.get_data(custom_path=custom_path)
280
+ spectra = self.get_data()
272
281
  reports = []
273
282
  for i, child_dir in enumerate(parent_dir.child_dirs):
274
283
  metd_report = self.get_report_details(child_dir, report_type)
@@ -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
- from typing import Union, Optional, List, Tuple, Dict
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, Err
16
-
17
- from ....analysis.process_report import AgilentReport, ReportType, CSVProcessor, TXTProcessor
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 AgilentHPLCChromatogram, AgilentChannelChromatogramData
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 Table, TableOperation, RegisterFlag, T
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
- time_passed = (time.time() - self.curr_run_starting_time)
204
- if time_passed > self.timeout:
205
- done_running = self.controller.check_if_not_running()
206
- enough_time_passed = time_passed >= self.timeout
207
- run_finished = enough_time_passed and done_running
208
- if run_finished:
209
- self._reset_time()
210
- return 0, run_finished
211
- return (time_passed / self.timeout), self.controller.check_if_not_running()
212
-
213
- def check_hplc_done_running(self) -> Result[Union[SequenceDataFiles, str], str]:
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: Return True if data can be read back, else False.
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
- wavelengthes = data_uv.ylabels
267
- data = data_uv.data.transpose()
268
- for (i, w) in enumerate(wavelengthes):
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, data[i])
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
- def get_spectrum(self, data_path: str, read_uv: bool = False):
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
- print(f"No data at channel: {channel}")
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
@@ -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 Union, Optional, List, Tuple
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 ReportType, AgilentReport
11
- from ..control.controllers import MethodController, SequenceController, CommunicationController
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
- data: Optional[str] = None) -> AgilentChannelChromatogramData:
212
+ custom_path: Optional[str] = None) -> Dict[str, AgilentHPLCChromatogram] | AgilentChannelChromatogramData:
204
213
  """
205
214
  Returns the last run method data.
206
215
 
207
- :param data: If you want to just load method data but from a file path. This file path must be the complete file path.
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
- return self.method_controller.get_data(custom_path=data, read_uv=read_uv)
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
- data: Optional[str] = None) -> List[AgilentChannelChromatogramData]:
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 data: If you want to just load sequence data but from a file path. This file path must be the complete file path.
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
- return self.sequence_controller.get_data(custom_path=data, read_uv=read_uv)
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."""
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass, field
2
- from typing import Optional, List
2
+ from typing import List, Optional
3
3
 
4
4
 
5
5
  @dataclass
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass, field
2
- from typing import Optional, List
2
+ from typing import List, Optional
3
3
  from xml.etree.ElementTree import QName
4
4
 
5
5
 
@@ -6,16 +6,13 @@ from dataclasses import dataclass
6
6
 
7
7
  import numpy as np
8
8
 
9
- from .parsing import CHFile
10
9
  from ..analysis import AbstractSpectrum
11
-
12
- # standard filenames for spectral data
13
- CHANNELS = {"A": "01", "B": "02", "C": "03", "D": "04"}
10
+ from .parsing import CHFile
14
11
 
15
12
  ACQUISITION_PARAMETERS = "acq.txt"
16
13
 
17
14
  # format used in acquisition parameters
18
- TIME_FORMAT = "%Y-%m-%d-%H-%M-%S"
15
+ TIME_FORMAT = "%Y-%m-%d %H-%M-%S"
19
16
  SEQUENCE_TIME_FORMAT = "%Y-%m-%d %H-%M"
20
17
 
21
18
 
@@ -1,6 +1,6 @@
1
1
  from dataclasses import dataclass
2
2
  from enum import Enum
3
- from typing import Union, Optional, List
3
+ from typing import List, Optional, Union
4
4
 
5
5
  from pychemstation.utils.tray_types import Tray
6
6
 
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
- from enum import Enum
4
- from typing import Union
5
3
  from dataclasses import dataclass
4
+ from enum import Enum
5
+ from typing import Union, Any, Type
6
6
 
7
7
 
8
8
  @dataclass
@@ -87,7 +87,7 @@ class HPLCErrorStatus(Enum):
87
87
  MALFORMED = "MALFORMED"
88
88
 
89
89
 
90
- def str_to_status(status: str) -> Union[HPLCAvailStatus, HPLCErrorStatus, HPLCRunningStatus]:
90
+ def str_to_status(status: str) -> Type[HPLCRunningStatus[Any] | HPLCErrorStatus[Any] | HPLCAvailStatus[Any]]:
91
91
  if HPLCErrorStatus.has_member_key(status):
92
92
  return HPLCErrorStatus[status]
93
93
  if HPLCRunningStatus.has_member_key(status):
@@ -2,11 +2,11 @@ from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
4
  from enum import Enum
5
- from typing import Union, Any, Optional
5
+ from typing import Any, Optional, Union
6
6
 
7
+ from ..generated import Signal
7
8
  from .injector_types import InjectorTable
8
9
  from .table_types import RegisterFlag
9
- from ..generated import Signal
10
10
 
11
11
 
12
12
  class PType(Enum):
@@ -13,6 +13,7 @@ I use it for file with version 130, genereted by an Agilent LC.
13
13
 
14
14
  import struct
15
15
  from struct import unpack
16
+
16
17
  import numpy as np
17
18
 
18
19
  # Constants used for binary file parsing
@@ -2,16 +2,16 @@ from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
4
  from enum import Enum
5
- from typing import Optional, Union
5
+ from typing import Optional, List
6
6
 
7
- from pychemstation.utils.tray_types import TenVialColumn, Tray
7
+ from pychemstation.utils.tray_types import Tray
8
8
 
9
9
 
10
10
  @dataclass
11
11
  class SequenceDataFiles:
12
12
  sequence_name: str
13
13
  dir: str
14
- child_dirs: Optional[list[str]] = None
14
+ child_dirs: Optional[List[str]] = None
15
15
 
16
16
 
17
17
  class SampleType(Enum):
@@ -6,7 +6,7 @@ analysis.
6
6
  import numpy as np
7
7
  import scipy
8
8
 
9
- from .utils import find_nearest_value_index
9
+ from ..utils.num_utils import find_nearest_value_index
10
10
 
11
11
 
12
12
  def create_binary_peak_map(data):
@@ -156,14 +156,14 @@ def filter_noisy_regions(y_data, peaks_regions):
156
156
  # compute the actual regions data points
157
157
  y_data_regions = []
158
158
  for region in peaks_regions:
159
- y_data_regions.append(y_data[region[0] : region[-1]])
159
+ y_data_regions.append(y_data[region[0]: region[-1]])
160
160
 
161
161
  # compute noise data regions, i.e. in between peak regions
162
162
  noise_data_regions = []
163
163
  for row, _ in enumerate(peaks_regions):
164
164
  try:
165
165
  noise_data_regions.append(
166
- y_data[peaks_regions[row][1] : peaks_regions[row + 1][0]]
166
+ y_data[peaks_regions[row][1]: peaks_regions[row + 1][0]]
167
167
  )
168
168
  except IndexError:
169
169
  # exception for the last row -> discard
@@ -1,10 +1,17 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pychemstation
3
- Version: 0.8.3
4
- Summary: Library to interact with Chemstation software, primarily used in Hein lab.
3
+ Version: 0.10.1
4
+ Summary: Library to interact with Chemstation software, primarily used in Hein lagit branch -mb
5
+ Project-URL: Documentation, https://pychemstation-e5a086.gitlab.io/pychemstation.html
6
+ Project-URL: Repository, https://gitlab.com/heingroup/device-api/pychemstation
5
7
  Author-email: lucyhao <hao.lucyy@gmail.com>
6
8
  License-File: LICENSE
7
- Requires-Python: >=3.8
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Requires-Python: >=3.10
8
15
  Requires-Dist: aghplctools>=4.8.8
9
16
  Requires-Dist: coverage>=7.6.1
10
17
  Requires-Dist: matplotlib>=3.7.5
@@ -27,8 +34,12 @@ Description-Content-Type: text/markdown
27
34
 
28
35
  [![PyPI Latest Release](https://img.shields.io/pypi/v/pychemstation.svg)](https://pypi.org/project/pychemstation/)
29
36
 
37
+ > **_NOTE:_** If you are running Python **3.8**, use versions 0.**8**.x. If you are running Python **>=3.10**, use
38
+ > version 0.**10**.x. You are welcome to use newer pychemstation versions with older Python versions, but functionality
39
+ > is not guaranteed!
40
+
30
41
  Unofficial Python package to control Agilent Chemstation; we are not affiliated with Agilent.
31
- Check out the [docs](https://hein-analytical-control-5e6e85.gitlab.io/) for usage instructions. This project is under
42
+ Check out the [docs](https://pychemstation-e5a086.gitlab.io/pychemstation.html) for usage instructions. This project is under
32
43
  active development, and breaking changes may occur at any moment.
33
44
 
34
45
  ## Getting started
@@ -110,6 +121,8 @@ Lucy Hao, Maria Politi
110
121
 
111
122
  - Adapted from [**AnalyticalLabware**](https://github.com/croningp/analyticallabware), created by members in the Cronin
112
123
  Group. Copyright © Cronin Group, used under the [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/) license.
113
- - Adapted from the [MACROS](https://github.com/Bourne-Group/HPLCMethodOptimisationGUI)
114
- used in [**Operator-free HPLC automated method development guided by Bayesian optimization**](https://pubs.rsc.org/en/content/articlelanding/2024/dd/d4dd00062e),
115
- created by members in the Bourne Group. Copyright © Bourne Group, used under the [MIT](https://opensource.org/license/mit) license.
124
+ - Adapted from the [MACROS](https://github.com/Bourne-Group/HPLCMethodOptimisationGUI) used in [**Operator-free HPLC
125
+ automated method development guided by Bayesian optimization
126
+ **](https://pubs.rsc.org/en/content/articlelanding/2024/dd/d4dd00062e),
127
+ created by members in the Bourne Group. Copyright © Bourne Group, used under
128
+ the [MIT](https://opensource.org/license/mit) license.
@@ -0,0 +1,37 @@
1
+ pychemstation/__init__.py,sha256=SpTl-Tg1B1HTyjNOE-8ue-N2wGnXN_2zl7RFUSxlkiM,33
2
+ pychemstation/analysis/__init__.py,sha256=Vi31PZ7fgIvyuIhkgCNvEYeV_jtUCa8FCrAGbpks7zc,83
3
+ pychemstation/analysis/base_spectrum.py,sha256=9WkOLk2qTAYTF1ALNUenVPoosOtBiLRvy2ni8zlGU5w,16540
4
+ pychemstation/analysis/process_report.py,sha256=ZOgcRUMGXdGMrMFcdzsSwdOk6OBp-PpcA83vSvnmVSg,11871
5
+ pychemstation/control/README.md,sha256=y73F-qh4g3k9Z9vBeQATqqhbwMfKB5MvGqJi5GgSUJQ,3357
6
+ pychemstation/control/__init__.py,sha256=Js79QczKZxDNZrzG1-4yl_whCoP2aw-yDAQJungiiic,100
7
+ pychemstation/control/hplc.py,sha256=f7NHcCWtc_ApasjU5VMQtEvWQxoIboi_-RU9dkjORrs,13215
8
+ pychemstation/control/controllers/README.md,sha256=S5cd4NJmPjs6TUH98BtPJJhiS1Lu-mxLCNS786ogOrQ,32
9
+ pychemstation/control/controllers/__init__.py,sha256=r7UU0u5zuJHO_KTqt-4Gy65BMlyXtxrdskiOhtO9Yw4,260
10
+ pychemstation/control/controllers/comm.py,sha256=AN3A3ThvIsOKWY7Kmb_tnE5pRUqI7O2ID8M54z_w-uE,7831
11
+ pychemstation/control/controllers/devices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ pychemstation/control/controllers/devices/device.py,sha256=JNBKVRka1I3LA1lElIeUO0j93BTK5IJufTPNq95OhNE,1473
13
+ pychemstation/control/controllers/devices/injector.py,sha256=s40jFd0B_wJn4ID6SgAk_F8WhnGbbflpiti4uwIhaSs,1950
14
+ pychemstation/control/controllers/tables/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ pychemstation/control/controllers/tables/method.py,sha256=LHoNRSTsSrrktghqNnU5KTRXDczcuGgqdqKEs_3sUXI,18609
16
+ pychemstation/control/controllers/tables/ms.py,sha256=JFD-tOhu8uRyKdl-E3-neRssii8MNqVRIlsrnFhNY_M,682
17
+ pychemstation/control/controllers/tables/sequence.py,sha256=DwX0wi5GhHmk8wnl89X2MKvqTSojLycV4IEvNjdVdWg,13400
18
+ pychemstation/control/controllers/tables/table.py,sha256=zMzsQgkLxM3LVe9w-OM8WjLZxTo9zrmBTNH182gAyh8,12750
19
+ pychemstation/generated/__init__.py,sha256=GAoZFAYbPVEJDkcOw3e1rgOqd7TCW0HyKNPM8OMehMg,1005
20
+ pychemstation/generated/dad_method.py,sha256=xTUiSCvkXcxBUhjVm1YZKu-tHs16k23pF-0xYrQSwWA,8408
21
+ pychemstation/generated/pump_method.py,sha256=797RsSDI2-QPf_BX8isZQx0O3aRx84EGIXJXhXw3IS0,12180
22
+ pychemstation/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ pychemstation/utils/chromatogram.py,sha256=2Los_ix_wAi4yxG_9neGRnNYPre9_uC1mrne3Ygit5c,3242
24
+ pychemstation/utils/injector_types.py,sha256=PXwJK1uXs8hlQ6dWIEbAGfk2BpQJQmN3SlUbL4ntZz0,822
25
+ pychemstation/utils/macro.py,sha256=Lh8aGcwo9sC2Sfc19Wgms5d3VgBLV3VXdvspqNHYE18,2931
26
+ pychemstation/utils/method_types.py,sha256=ZOFMJ7wpqWBRJNIvOux-7Ou4nJVSuyWRHrd37wMnPa0,1638
27
+ pychemstation/utils/num_utils.py,sha256=rgpTJTrpsiBANbtsfys9xj0sqlTe__3J0OSeoygaQTM,2081
28
+ pychemstation/utils/parsing.py,sha256=iFdnie-v0u5JI4cctJye_yhWQxHgy72_PWZ7w57Ltvg,9318
29
+ pychemstation/utils/pump_types.py,sha256=HWQHxscGn19NTrfYBwQRCO2VcYfwyko7YfBO5uDhEm4,93
30
+ pychemstation/utils/sequence_types.py,sha256=WyJWL18Q86TgoUpYH2_CevoTZuhcui0EnyHYdrp3Nmo,1070
31
+ pychemstation/utils/spec_utils.py,sha256=MQZPDwCYZRfkEhNJQUt74huPexXBlJ3W4o7_230JWcE,10210
32
+ pychemstation/utils/table_types.py,sha256=7txqW_oNpkh4venSkGEtreVe6UV9dzNB1DTrIeTkQHA,3217
33
+ pychemstation/utils/tray_types.py,sha256=eOO-muUjadyvCM8JnYAZVyxJeyYBlENP1zXiFskAFbs,5049
34
+ pychemstation-0.10.1.dist-info/METADATA,sha256=gWFw70cbvfeIPV1T3IJDLDktm4FjjXn1JvPfTDQZoDw,5274
35
+ pychemstation-0.10.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
36
+ pychemstation-0.10.1.dist-info/licenses/LICENSE,sha256=9bdF75gIf1MecZ7oymqWgJREVz7McXPG-mjqrTmzzD8,18658
37
+ pychemstation-0.10.1.dist-info/RECORD,,
@@ -1,37 +0,0 @@
1
- pychemstation/__init__.py,sha256=SpTl-Tg1B1HTyjNOE-8ue-N2wGnXN_2zl7RFUSxlkiM,33
2
- pychemstation/analysis/__init__.py,sha256=EWoU47iyn9xGS-b44zK9eq50bSjOV4AC5dvt420YMI4,44
3
- pychemstation/analysis/base_spectrum.py,sha256=dPULGlr-sMHNMDIdyzlvX8taw_nkBliKTb00lNI8cwk,16517
4
- pychemstation/analysis/process_report.py,sha256=wysKaA06UeK_9ba2SZc9maP4P-HQfZDMD9NNt21kWcY,11850
5
- pychemstation/analysis/spec_utils.py,sha256=UOo9hJR3evJfmaohEEsyb7aq6X996ofuUfu-GKjiDi8,10201
6
- pychemstation/analysis/utils.py,sha256=rgpTJTrpsiBANbtsfys9xj0sqlTe__3J0OSeoygaQTM,2081
7
- pychemstation/control/README.md,sha256=7Q0rY014y7Qq8wfL7GSQG0l2P1PXfmgB0qZ2YkkE7n0,3350
8
- pychemstation/control/__init__.py,sha256=4xTy8X-mkn_PPZKr7w9rnj1wZhtmTesbQptPhpYmKXs,64
9
- pychemstation/control/hplc.py,sha256=E_QFMOqUabiqCO6Pu4lZYTsiWhdQULw4AqyKPwiQx90,12628
10
- pychemstation/control/controllers/README.md,sha256=S5cd4NJmPjs6TUH98BtPJJhiS1Lu-mxLCNS786ogOrQ,32
11
- pychemstation/control/controllers/__init__.py,sha256=LuFEsVGN5sXuHW_DG61DWBDJ_fU4dMls4bQJ5XNRaok,166
12
- pychemstation/control/controllers/comm.py,sha256=fRCFh3Ye-HnoGaur69NRdkLHNtdlMoJjIOTqeJe1tAk,7720
13
- pychemstation/control/controllers/devices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- pychemstation/control/controllers/devices/device.py,sha256=d9SwasjXTXplkRK5eH3asrNPo3BBZmAz6CKBYAuKSIc,1249
15
- pychemstation/control/controllers/devices/injector.py,sha256=ynPQtvMFt1iK0LQBf4ZEYdxJCyavmashXwyCQbmRjuw,5542
16
- pychemstation/control/controllers/tables/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- pychemstation/control/controllers/tables/method.py,sha256=sX83sHlnaAm8Wop_d28RDAk4DBPvQWduH3Atodtmw4A,17714
18
- pychemstation/control/controllers/tables/ms.py,sha256=JFD-tOhu8uRyKdl-E3-neRssii8MNqVRIlsrnFhNY_M,682
19
- pychemstation/control/controllers/tables/sequence.py,sha256=1bZdoVK877KuAwKTjo-NWvOU8rHxx8cQBL4I71ZN4EM,13105
20
- pychemstation/control/controllers/tables/table.py,sha256=Adis9584LJqP4yjLj5XrIxqq1jzdBiDieOG4vc90EIU,12327
21
- pychemstation/generated/__init__.py,sha256=GAoZFAYbPVEJDkcOw3e1rgOqd7TCW0HyKNPM8OMehMg,1005
22
- pychemstation/generated/dad_method.py,sha256=zfS9op450CRSGPKkUr9qUyPBbND06b9N8SUU9j4cosM,8408
23
- pychemstation/generated/pump_method.py,sha256=c_FB14rgODZyH5eDb3kxZAI77xRj1HMpQkE2R6MypNA,12180
24
- pychemstation/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
- pychemstation/utils/chromatogram.py,sha256=s6mqru88E9YQGLbdr2Thm2plf8urCFoHHkMqW6_1Rz4,3338
26
- pychemstation/utils/injector_types.py,sha256=hTgC-4xnO-EXOLOsCZ0L6TX4KUsB14Q-5jU4_hCzmfM,822
27
- pychemstation/utils/macro.py,sha256=WgXGR8fgyR_QhRML9NUD3oZZBT4LLSI9uwuragHEe2k,2904
28
- pychemstation/utils/method_types.py,sha256=5FK7RThLhaQcLrzRi_qLnlPqZuGPtwwipP6eMoq0kpE,1638
29
- pychemstation/utils/parsing.py,sha256=bnFIsZZwFy9NKzVUf517yN-ogzQbm0hp_aho3KUD6Is,9317
30
- pychemstation/utils/pump_types.py,sha256=HWQHxscGn19NTrfYBwQRCO2VcYfwyko7YfBO5uDhEm4,93
31
- pychemstation/utils/sequence_types.py,sha256=x2EClcq6ROdzeLZg63XcXXTknwl2aZ48Vuyru0xZjgA,1086
32
- pychemstation/utils/table_types.py,sha256=7txqW_oNpkh4venSkGEtreVe6UV9dzNB1DTrIeTkQHA,3217
33
- pychemstation/utils/tray_types.py,sha256=eOO-muUjadyvCM8JnYAZVyxJeyYBlENP1zXiFskAFbs,5049
34
- pychemstation-0.8.3.dist-info/METADATA,sha256=vfeuRByzef7lYuAQgYyON587lphk76NR3cPOQZyhwfQ,4575
35
- pychemstation-0.8.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
36
- pychemstation-0.8.3.dist-info/licenses/LICENSE,sha256=9bdF75gIf1MecZ7oymqWgJREVz7McXPG-mjqrTmzzD8,18658
37
- pychemstation-0.8.3.dist-info/RECORD,,
File without changes