pychemstation 0.10.10__py3-none-any.whl → 0.10.12__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.
@@ -0,0 +1,61 @@
1
+ from typing import List, Dict
2
+
3
+ from ....control.controllers import CommunicationController
4
+ from ....utils.abc_tables.device import DeviceController
5
+ from ....utils.macro import Command
6
+ from ....utils.method_types import Param, PType
7
+ from ....utils.table_types import Device, Table, RegisterFlag
8
+
9
+
10
+ class ColumnController(DeviceController):
11
+ def __init__(
12
+ self, controller: CommunicationController, table: Table | Device, offline: bool
13
+ ):
14
+ super().__init__(controller, table, offline)
15
+ if not self.offline:
16
+ self.display_to_internal: Dict[str, str] = {
17
+ display_name: real_name
18
+ for display_name, real_name in zip(
19
+ self.check_available_column_positions(),
20
+ self._internal_column_positions(),
21
+ )
22
+ }
23
+ self.internal_to_display: Dict[str, str] = dict(
24
+ map(reversed, self.display_to_internal.items()) # type: ignore
25
+ )
26
+
27
+ def check_column_position(self):
28
+ return self.internal_to_display[
29
+ self._read_str_param(register_flag=RegisterFlag.COLUMN_POSITION)
30
+ ]
31
+
32
+ def _internal_column_positions(self) -> List[str]:
33
+ return self._read_str_param(
34
+ register_flag=RegisterFlag.AVAIL_COLUMN_POSITIONS
35
+ ).split("|")
36
+
37
+ def check_available_column_positions(self) -> List[str]:
38
+ return self._read_str_param(
39
+ register_flag=RegisterFlag.AVAIL_COLUMN_DISPLAY_VALUES
40
+ ).split("|")
41
+
42
+ def change_column_position(self, column: str):
43
+ if column not in self.display_to_internal.keys():
44
+ raise ValueError(f"Please use one of: {self.display_to_internal.keys()}")
45
+ self._update_param(
46
+ Param(
47
+ ptype=PType.STR,
48
+ chemstation_key=RegisterFlag.COLUMN_POSITION,
49
+ val=self.display_to_internal[column],
50
+ ),
51
+ )
52
+
53
+ def turn_off(self):
54
+ self.sleepy_send(Command.COLUMN_OFF_CMD)
55
+ pass
56
+
57
+ def turn_on(self):
58
+ self.sleepy_send(Command.COLUMN_ON_CMD)
59
+
60
+ def download(self):
61
+ self.sleepy_send("DownloadRCMethod THM1")
@@ -0,0 +1,85 @@
1
+ from __future__ import annotations
2
+
3
+ import warnings
4
+
5
+ from ...controllers import CommunicationController
6
+ from ....utils.abc_tables.device import DeviceController
7
+ from ....utils.device_types import DADChannels, DADChannel
8
+ from ....utils.macro import Command
9
+ from ....utils.method_types import Param, PType
10
+ from ....utils.table_types import RegisterFlag, Table, Device
11
+
12
+
13
+ class DADController(DeviceController):
14
+ def __init__(
15
+ self, controller: CommunicationController, table: Table | Device, offline: bool
16
+ ):
17
+ super().__init__(controller, table, offline)
18
+
19
+ def download(self):
20
+ self.sleepy_send("DownloadRCMethod DAD1")
21
+
22
+ def turn_off(self):
23
+ self.send(Command.LAMP_OFF_CMD)
24
+
25
+ def turn_on(self):
26
+ self.send(Command.LAMP_ON_CMD)
27
+
28
+ def load_wavelengths(self) -> DADChannels:
29
+ return DADChannels(
30
+ A=self._read_num_param(RegisterFlag.SIGNAL_A),
31
+ A_ON=bool(self._read_num_param(RegisterFlag.SIGNAL_A_USED)),
32
+ B=self._read_num_param(RegisterFlag.SIGNAL_B),
33
+ B_ON=bool(self._read_num_param(RegisterFlag.SIGNAL_B_USED)),
34
+ C=self._read_num_param(RegisterFlag.SIGNAL_C),
35
+ C_ON=bool(self._read_num_param(RegisterFlag.SIGNAL_C_USED)),
36
+ D=self._read_num_param(RegisterFlag.SIGNAL_D),
37
+ D_ON=bool(self._read_num_param(RegisterFlag.SIGNAL_D_USED)),
38
+ E=self._read_num_param(RegisterFlag.SIGNAL_E),
39
+ E_ON=bool(self._read_num_param(RegisterFlag.SIGNAL_E_USED)),
40
+ )
41
+
42
+ def edit_wavelength(self, signal: int, wavelength: DADChannel):
43
+ warnings.warn("You may need to check that the wavelength is calibrated.")
44
+ register = RegisterFlag.SIGNAL_A
45
+ match wavelength:
46
+ case DADChannel.A:
47
+ register = RegisterFlag.SIGNAL_A
48
+ case DADChannel.B:
49
+ register = RegisterFlag.SIGNAL_B
50
+ case DADChannel.C:
51
+ register = RegisterFlag.SIGNAL_C
52
+ case DADChannel.D:
53
+ register = RegisterFlag.SIGNAL_D
54
+ case DADChannel.E:
55
+ register = RegisterFlag.SIGNAL_E
56
+ self._update_param(
57
+ Param(
58
+ ptype=PType.NUM,
59
+ val=signal,
60
+ chemstation_key=register,
61
+ )
62
+ )
63
+ self.download()
64
+
65
+ def set_wavelength_usage(self, wavelength: DADChannel, on: bool):
66
+ register = RegisterFlag.SIGNAL_A_USED
67
+ match wavelength:
68
+ case DADChannel.A:
69
+ register = RegisterFlag.SIGNAL_A_USED
70
+ case DADChannel.B:
71
+ register = RegisterFlag.SIGNAL_B_USED
72
+ case DADChannel.C:
73
+ register = RegisterFlag.SIGNAL_C_USED
74
+ case DADChannel.D:
75
+ register = RegisterFlag.SIGNAL_D_USED
76
+ case DADChannel.E:
77
+ register = RegisterFlag.SIGNAL_E_USED
78
+ self._update_param(
79
+ Param(
80
+ ptype=PType.NUM,
81
+ val=int(on),
82
+ chemstation_key=register,
83
+ )
84
+ )
85
+ self.download()
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import warnings
4
+
3
5
  from ....control.controllers import CommunicationController
4
6
  from ....utils.abc_tables.device import DeviceController
5
7
  from ....utils.injector_types import (
@@ -15,7 +17,7 @@ from ....utils.injector_types import (
15
17
  DrawDefaultVolume,
16
18
  DrawDefaultLocation,
17
19
  )
18
- from ....utils.macro import Response
20
+ from ....utils.method_types import Param, PType
19
21
  from ....utils.table_types import RegisterFlag, Table
20
22
  from ....utils.tray_types import Tray, FiftyFourVialPlate, VialBar, LocationPlus
21
23
 
@@ -26,6 +28,21 @@ class InjectorController(DeviceController):
26
28
  ):
27
29
  super().__init__(controller, table, offline)
28
30
 
31
+ def turn_off(self):
32
+ warnings.warn("Injector has no on/off state.")
33
+
34
+ def turn_on(self):
35
+ warnings.warn("Injector has no on/off state.")
36
+
37
+ def change_injection_volume(self, new_injection_volume: float):
38
+ self._update_param(
39
+ param=Param(
40
+ ptype=PType.NUM,
41
+ val=new_injection_volume,
42
+ chemstation_key=RegisterFlag.INJECTION_VOLUME,
43
+ )
44
+ )
45
+
29
46
  def try_vial_location(self, val: str) -> Tray:
30
47
  try:
31
48
  return FiftyFourVialPlate.from_str(val)
@@ -93,14 +110,5 @@ class InjectorController(DeviceController):
93
110
  raise ValueError("No valid function found.")
94
111
 
95
112
  def load(self) -> InjectorTable | None:
96
- rows = self.get_num_rows()
97
- if rows.is_ok():
98
- row_response = rows.value
99
- if isinstance(row_response, Response):
100
- return InjectorTable(
101
- functions=[
102
- self.get_row(i + 1)
103
- for i in range(int(row_response.num_response))
104
- ]
105
- )
106
- raise ValueError("Couldn't read injector table rows.")
113
+ rows = self.get_row_count_safely()
114
+ return InjectorTable(functions=[self.get_row(i + 1) for i in range(rows)])
@@ -0,0 +1,131 @@
1
+ from __future__ import annotations
2
+
3
+ import warnings
4
+
5
+ from ....utils.macro import Command
6
+ from ....control.controllers import CommunicationController
7
+ from ....utils.abc_tables.device import DeviceController
8
+ from ....utils.device_types import (
9
+ SolventBottle,
10
+ MaybeBottle,
11
+ MaybePumpPosition,
12
+ PumpValve,
13
+ PumpPosition,
14
+ )
15
+ from ....utils.table_types import Table, RegisterFlag, Device
16
+
17
+
18
+ class PumpController(DeviceController):
19
+ def __init__(
20
+ self, controller: CommunicationController, table: Table | Device, offline: bool
21
+ ):
22
+ super().__init__(controller, table, offline)
23
+ self.A_position: MaybePumpPosition = (
24
+ self.load_pump_position(PumpValve.A) if not offline else None
25
+ )
26
+ self.B_position: MaybePumpPosition = (
27
+ self.load_pump_position(PumpValve.B) if not offline else None
28
+ )
29
+ self.A1: MaybeBottle = None
30
+ self.A2: MaybeBottle = None
31
+ self.B1: MaybeBottle = None
32
+ self.B2: MaybeBottle = None
33
+ self.waste_bottle: MaybeBottle = None
34
+
35
+ def load_pump_position(self, pump_valve: PumpValve) -> PumpPosition:
36
+ match pump_valve:
37
+ case PumpValve.A:
38
+ return PumpPosition.from_str(
39
+ self._read_str_param(RegisterFlag.PUMPCHANNEL_SELECTION)
40
+ )
41
+ case PumpValve.B:
42
+ return PumpPosition.from_str(
43
+ self._read_str_param(RegisterFlag.PUMPCHANNEL2_SELECTION)
44
+ )
45
+ case _:
46
+ raise ValueError("Expected one of PumpValve.A or PumpValve.B")
47
+
48
+ def load_bottles(self):
49
+ self.A1 = self.get_solvent_bottle_a1()
50
+ self.A2 = self.get_solvent_bottle_a2()
51
+ self.B1 = self.get_solvent_bottle_b1()
52
+ self.B2 = self.get_solvent_bottle_b2()
53
+
54
+ @property
55
+ def rinse_method(self):
56
+ return self._rinse_method
57
+
58
+ @rinse_method.setter
59
+ def rinse_method(self, new_rinse_method: str):
60
+ self._rinse_method = new_rinse_method
61
+
62
+ def turn_off(self):
63
+ self.send(Command.PUMP_OFF_CMD)
64
+
65
+ def turn_on(self):
66
+ self.send(Command.PUMP_ON_CMD)
67
+
68
+ def get_solvent_bottle_a1(self) -> SolventBottle:
69
+ return SolventBottle(
70
+ absolute_filled=self._read_num_param(
71
+ RegisterFlag.BOTTLE_A1_ABSOLUTE_FILLING
72
+ ),
73
+ percent_filled=self._read_num_param(RegisterFlag.BOTTLE_A1_PERCENT_FILLING),
74
+ max_volume=self._read_num_param(RegisterFlag.BOTTLE_A1_MAX),
75
+ in_use=self.A_position == PumpPosition.ONE,
76
+ user_name=self._read_str_param(RegisterFlag.BOTTLE_A1_USER_NAME),
77
+ type=PumpValve.A,
78
+ )
79
+
80
+ def get_solvent_bottle_a2(self) -> SolventBottle:
81
+ return SolventBottle(
82
+ absolute_filled=self._read_num_param(
83
+ RegisterFlag.BOTTLE_A2_ABSOLUTE_FILLING
84
+ ),
85
+ percent_filled=self._read_num_param(RegisterFlag.BOTTLE_A2_PERCENT_FILLING),
86
+ max_volume=self._read_num_param(RegisterFlag.BOTTLE_A2_MAX),
87
+ in_use=self.A_position == PumpPosition.TWO,
88
+ user_name=self._read_str_param(RegisterFlag.BOTTLE_A2_USER_NAME),
89
+ type=PumpValve.A,
90
+ )
91
+
92
+ def get_solvent_bottle_b1(self) -> SolventBottle:
93
+ return SolventBottle(
94
+ absolute_filled=self._read_num_param(
95
+ RegisterFlag.BOTTLE_B1_ABSOLUTE_FILLING
96
+ ),
97
+ percent_filled=self._read_num_param(RegisterFlag.BOTTLE_B1_PERCENT_FILLING),
98
+ max_volume=self._read_num_param(RegisterFlag.BOTTLE_B1_MAX),
99
+ in_use=self.A_position == PumpPosition.ONE,
100
+ user_name=self._read_str_param(RegisterFlag.BOTTLE_B1_USER_NAME),
101
+ type=PumpValve.B,
102
+ )
103
+
104
+ def get_solvent_bottle_b2(self) -> SolventBottle:
105
+ return SolventBottle(
106
+ absolute_filled=self._read_num_param(
107
+ RegisterFlag.BOTTLE_B2_ABSOLUTE_FILLING
108
+ ),
109
+ percent_filled=self._read_num_param(RegisterFlag.BOTTLE_B2_PERCENT_FILLING),
110
+ max_volume=self._read_num_param(RegisterFlag.BOTTLE_B2_MAX),
111
+ in_use=self.A_position == PumpPosition.TWO,
112
+ user_name=self._read_str_param(RegisterFlag.BOTTLE_B2_USER_NAME),
113
+ type=PumpValve.B,
114
+ )
115
+
116
+ def get_waste_bottle_stats(self):
117
+ max_vol = None
118
+ try:
119
+ max_vol = self._read_num_param(RegisterFlag.WASTE_BOTTLE_MAX)
120
+ except RuntimeError:
121
+ warnings.warn(
122
+ "No maximum volume available! All other SolventBottle parameters may not be reliable."
123
+ )
124
+ self.waste_bottle = SolventBottle(
125
+ absolute_filled=self._read_num_param(RegisterFlag.WASTE_BOTTLE_PERCENT),
126
+ percent_filled=self._read_num_param(RegisterFlag.WASTE_BOTTLE_ABSOLUTE),
127
+ max_volume=max_vol,
128
+ in_use=True,
129
+ user_name="Waste Bottle",
130
+ type=None,
131
+ )
@@ -0,0 +1,27 @@
1
+ from ....control.controllers import CommunicationController
2
+ from ....utils.abc_tables.device import DeviceController
3
+ from ....utils.table_types import Table, Device, RegisterFlag
4
+ from ....utils.tray_types import Tray, VialBar, FiftyFourVialPlate
5
+
6
+
7
+ class SampleInfo(DeviceController):
8
+ def turn_off(self):
9
+ raise NotImplementedError
10
+
11
+ def turn_on(self):
12
+ raise NotImplementedError
13
+
14
+ def __init__(
15
+ self, controller: CommunicationController, table: Table | Device, offline: bool
16
+ ):
17
+ super().__init__(controller, table, offline)
18
+
19
+ def get_location(self) -> Tray:
20
+ location = self._read_str_param(RegisterFlag.VIAL_NUMBER)
21
+ try:
22
+ return FiftyFourVialPlate.from_int(location)
23
+ except ValueError:
24
+ try:
25
+ return VialBar(location)
26
+ except ValueError:
27
+ raise ValueError("Could not read vial location.")
@@ -9,19 +9,20 @@ from __future__ import annotations
9
9
  import os.path
10
10
  from typing import Dict, List, Optional, Tuple, Union
11
11
 
12
- from pychemstation.analysis.chromatogram import AgilentChannelChromatogramData
13
- from pychemstation.analysis.chromatogram import (
14
- AgilentHPLCChromatogram,
15
- )
12
+ from .controllers.devices.column import ColumnController
13
+ from .controllers.devices.dad import DADController
16
14
  from .controllers.devices.injector import InjectorController
17
15
  from .controllers.data_aq.sequence import SequenceController, MethodController
16
+ from .controllers.devices.pump import PumpController
17
+ from .controllers.devices.sample_info import SampleInfo
18
+ from ..analysis import AgilentHPLCChromatogram, AgilentChannelChromatogramData
18
19
  from ..analysis.process_report import AgilentReport, ReportType
19
20
  from ..control.controllers import CommunicationController
20
21
  from ..utils.injector_types import InjectorTable
21
22
  from ..utils.macro import Command, Response, Status
22
23
  from ..utils.method_types import MethodDetails
23
24
  from ..utils.sequence_types import SequenceTable, SequenceDataFiles
24
- from ..utils.table_types import Table
25
+ from ..utils.table_types import Table, Device
25
26
 
26
27
 
27
28
  class HPLCController:
@@ -32,7 +33,15 @@ class HPLCController:
32
33
 
33
34
  INJECTOR_TABLE = Table(register="RCWLS1Pretreatment[1]", name="InstructionTable")
34
35
 
35
- MSD_TABLE = Table(register="MSACQINFO[1]", name="SprayChamber")
36
+ PUMP_DEVICE = Device(register="RCPMP1Status")
37
+
38
+ INJECTOR_DEVICE = Device(register="RCWLS1Method")
39
+
40
+ SAMPLE_INFO = Table(register="_CONFIG", name="SampleInfo")
41
+
42
+ COLUMN_TEMP_DEVICE = Device(register="RCTHM1Method")
43
+
44
+ DAD_DEVICE = Device(register="RCDAD1Method")
36
45
 
37
46
  def __init__(
38
47
  self,
@@ -68,9 +77,21 @@ class HPLCController:
68
77
  data_dirs=data_dirs,
69
78
  table=self.METHOD_TIMETABLE,
70
79
  offline=offline,
71
- injector_controller=InjectorController(
80
+ injector=InjectorController(
72
81
  controller=self.comm, table=self.INJECTOR_TABLE, offline=offline
73
82
  ),
83
+ pump=PumpController(
84
+ controller=self.comm, table=self.PUMP_DEVICE, offline=offline
85
+ ),
86
+ dad=DADController(
87
+ controller=self.comm, table=self.DAD_DEVICE, offline=offline
88
+ ),
89
+ column=ColumnController(
90
+ controller=self.comm, table=self.COLUMN_TEMP_DEVICE, offline=offline
91
+ ),
92
+ sample_info=SampleInfo(
93
+ controller=self.comm, table=self.SAMPLE_INFO, offline=offline
94
+ ),
74
95
  )
75
96
  self.sequence_controller: SequenceController = SequenceController(
76
97
  controller=self.comm,
@@ -304,7 +325,10 @@ class HPLCController:
304
325
  return self.sequence_controller.load()
305
326
 
306
327
  def load_injector_program(self) -> InjectorTable | None:
307
- return self.method_controller.injector_controller.load()
328
+ return self.method_controller.injector.load()
329
+
330
+ def load_sample_location(self):
331
+ self.method_controller.get_location()
308
332
 
309
333
  def standby(self):
310
334
  """Switches all modules in standby mode. All lamps and pumps are switched off."""
@@ -316,19 +340,19 @@ class HPLCController:
316
340
 
317
341
  def lamp_on(self):
318
342
  """Turns the UV lamp on."""
319
- self.send(Command.LAMP_ON_CMD)
343
+ self.method_controller.dad.turn_on()
320
344
 
321
345
  def lamp_off(self):
322
346
  """Turns the UV lamp off."""
323
- self.send(Command.LAMP_OFF_CMD)
347
+ self.method_controller.dad.turn_off()
324
348
 
325
349
  def pump_on(self):
326
350
  """Turns on the pump on."""
327
- self.send(Command.PUMP_ON_CMD)
351
+ self.method_controller.pump.turn_on()
328
352
 
329
353
  def pump_off(self):
330
354
  """Turns the pump off."""
331
- self.send(Command.PUMP_OFF_CMD)
355
+ self.method_controller.pump.turn_off()
332
356
 
333
357
  def instrument_off(self):
334
358
  """Shuts the entire instrument off, including pumps, lamps, thermostat."""
@@ -1,9 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
- from abc import ABC
3
+ from abc import ABC, abstractmethod
4
4
 
5
5
  from .table import ABCTableController
6
- from ..table_types import Table
6
+ from ..table_types import Table, Device
7
7
  from ...control.controllers import CommunicationController
8
8
 
9
9
 
@@ -15,8 +15,11 @@ class DeviceController(ABCTableController, ABC):
15
15
  :param offline: whether the communication controller is online.
16
16
  """
17
17
 
18
+ def get_row(self, row: int):
19
+ raise NotImplementedError
20
+
18
21
  def __init__(
19
- self, controller: CommunicationController, table: Table, offline: bool
22
+ self, controller: CommunicationController, table: Table | Device, offline: bool
20
23
  ):
21
24
  super().__init__(controller=controller, table=table)
22
25
  self.offline = offline
@@ -25,3 +28,14 @@ class DeviceController(ABCTableController, ABC):
25
28
  if cls is ABCTableController:
26
29
  raise TypeError(f"only children of '{cls.__name__}' may be instantiated")
27
30
  return object.__new__(cls)
31
+
32
+ def download(self):
33
+ raise NotImplementedError
34
+
35
+ @abstractmethod
36
+ def turn_off(self):
37
+ pass
38
+
39
+ @abstractmethod
40
+ def turn_on(self):
41
+ pass