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.
Files changed (57) hide show
  1. pychemstation/analysis/__init__.py +4 -0
  2. pychemstation/analysis/base_spectrum.py +9 -9
  3. pychemstation/analysis/process_report.py +13 -7
  4. pychemstation/analysis/utils.py +1 -3
  5. pychemstation/control/__init__.py +4 -0
  6. pychemstation/control/comm.py +206 -0
  7. pychemstation/control/controllers/__init__.py +6 -0
  8. pychemstation/control/controllers/comm.py +12 -5
  9. pychemstation/control/controllers/devices/column.py +12 -0
  10. pychemstation/control/controllers/devices/dad.py +0 -0
  11. pychemstation/control/controllers/devices/device.py +10 -7
  12. pychemstation/control/controllers/devices/injector.py +18 -84
  13. pychemstation/control/controllers/devices/pump.py +43 -0
  14. pychemstation/control/controllers/method.py +338 -0
  15. pychemstation/control/controllers/sequence.py +190 -0
  16. pychemstation/control/controllers/table_controller.py +266 -0
  17. pychemstation/control/controllers/tables/method.py +35 -13
  18. pychemstation/control/controllers/tables/sequence.py +47 -38
  19. pychemstation/control/controllers/tables/table.py +46 -30
  20. pychemstation/control/hplc.py +27 -11
  21. pychemstation/control/table/__init__.py +3 -0
  22. pychemstation/control/table/method.py +274 -0
  23. pychemstation/control/table/sequence.py +210 -0
  24. pychemstation/control/table/table_controller.py +201 -0
  25. pychemstation/generated/dad_method.py +1 -1
  26. pychemstation/generated/pump_method.py +1 -1
  27. pychemstation/utils/chromatogram.py +2 -5
  28. pychemstation/utils/injector_types.py +1 -1
  29. pychemstation/utils/macro.py +3 -3
  30. pychemstation/utils/method_types.py +2 -2
  31. pychemstation/utils/num_utils.py +65 -0
  32. pychemstation/utils/parsing.py +1 -0
  33. pychemstation/utils/sequence_types.py +3 -3
  34. pychemstation/utils/spec_utils.py +304 -0
  35. {pychemstation-0.8.2.dist-info → pychemstation-0.10.0.dist-info}/METADATA +19 -7
  36. pychemstation-0.10.0.dist-info/RECORD +62 -0
  37. {pychemstation-0.8.2.dist-info → pychemstation-0.10.0.dist-info}/WHEEL +2 -1
  38. pychemstation-0.10.0.dist-info/top_level.txt +2 -0
  39. tests/__init__.py +0 -0
  40. tests/constants.py +134 -0
  41. tests/test_comb.py +136 -0
  42. tests/test_comm.py +65 -0
  43. tests/test_inj.py +39 -0
  44. tests/test_method.py +99 -0
  45. tests/test_nightly.py +80 -0
  46. tests/test_offline_stable.py +69 -0
  47. tests/test_online_stable.py +275 -0
  48. tests/test_proc_rep.py +52 -0
  49. tests/test_runs_stable.py +225 -0
  50. tests/test_sequence.py +125 -0
  51. tests/test_stable.py +276 -0
  52. pychemstation/control/README.md +0 -124
  53. pychemstation/control/controllers/README.md +0 -1
  54. pychemstation/out.txt +0 -145
  55. pychemstation/tests.ipynb +0 -310
  56. pychemstation-0.8.2.dist-info/RECORD +0 -39
  57. {pychemstation-0.8.2.dist-info → pychemstation-0.10.0.dist-info}/licenses/LICENSE +0 -0
@@ -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,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) -> Tuple[float, int]:
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.
@@ -2,3 +2,7 @@
2
2
  .. include:: README.md
3
3
  """
4
4
  from .hplc import HPLCController
5
+
6
+ __all__ = [
7
+ 'HPLCController',
8
+ ]
@@ -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)
@@ -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
 
@@ -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, 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
+
@@ -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