pychemstation 0.5.2__py3-none-any.whl → 0.5.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,10 +8,10 @@ import time
8
8
  from .table_controller import TableController
9
9
  from ...control import CommunicationController
10
10
  from ...utils.chromatogram import SEQUENCE_TIME_FORMAT, AgilentHPLCChromatogram
11
- from ...utils.constants import SEQUENCE_TABLE
12
11
  from ...utils.macro import Command
13
- from ...utils.sequence_types import SequenceTable, SequenceEntry, SequenceDataFiles
14
- from ...utils.table_types import TableOperation, RegisterFlag
12
+ from ...utils.sequence_types import SequenceTable, SequenceEntry, SequenceDataFiles, InjectionSource, SampleType
13
+ from ...utils.table_types import TableOperation, RegisterFlag, Table
14
+ from ...utils.tray_types import TenColumn
15
15
 
16
16
 
17
17
  class SequenceController(TableController):
@@ -19,8 +19,36 @@ class SequenceController(TableController):
19
19
  Class containing sequence related logic
20
20
  """
21
21
 
22
- def __init__(self, controller: CommunicationController, src: str, data_dir: str):
23
- super().__init__(controller, src, data_dir)
22
+ def __init__(self, controller: CommunicationController, src: str, data_dir: str, table: Table, method_dir: str):
23
+ self.method_dir = method_dir
24
+ super().__init__(controller, src, data_dir, table)
25
+
26
+ def load(self) -> SequenceTable:
27
+ rows = self.get_num_rows()
28
+ self.send(Command.GET_SEQUENCE_CMD)
29
+ seq_name = self.receive()
30
+
31
+ if rows.is_ok() and seq_name.is_ok():
32
+ return SequenceTable(
33
+ name=seq_name.ok_value.string_response.partition(".S")[0],
34
+ rows=[self.get_row(r + 1) for r in range(int(rows.ok_value.num_response))])
35
+ raise RuntimeError(rows.err_value)
36
+
37
+ def get_row(self, row: int) -> SequenceEntry:
38
+ sample_name = self.get_text(row, RegisterFlag.NAME)
39
+ vial_location = int(self.get_num(row, RegisterFlag.VIAL_LOCATION))
40
+ method = self.get_text(row, RegisterFlag.METHOD)
41
+ num_inj = int(self.get_num(row, RegisterFlag.NUM_INJ))
42
+ inj_vol = int(self.get_text(row, RegisterFlag.INJ_VOL))
43
+ inj_source = InjectionSource(self.get_text(row, RegisterFlag.INJ_SOR))
44
+ sample_type = SampleType(self.get_num(row, RegisterFlag.SAMPLE_TYPE))
45
+ return SequenceEntry(sample_name=sample_name,
46
+ vial_location=vial_location,
47
+ method=None if len(method) == 0 else method,
48
+ num_inj=num_inj,
49
+ inj_vol=inj_vol,
50
+ inj_source=inj_source,
51
+ sample_type=sample_type, )
24
52
 
25
53
  def switch(self, seq_name: str):
26
54
  """
@@ -34,13 +62,7 @@ class SequenceController(TableController):
34
62
  time.sleep(2)
35
63
  self.send(Command.GET_SEQUENCE_CMD)
36
64
  time.sleep(2)
37
- # check that method switched
38
- for _ in range(10):
39
- try:
40
- parsed_response = self.receive().splitlines()[1].split()[1:][0]
41
- break
42
- except IndexError:
43
- continue
65
+ parsed_response = self.receive().value.string_response
44
66
 
45
67
  assert parsed_response == f"{seq_name}.S", "Switching sequence failed."
46
68
 
@@ -51,120 +73,138 @@ class SequenceController(TableController):
51
73
 
52
74
  :param sequence_table:
53
75
  """
54
- self.send("Local Rows")
55
- self.sleep(1)
56
- self.delete_table(SEQUENCE_TABLE)
57
- self.sleep(1)
58
- self.new_table(SEQUENCE_TABLE)
59
- self.sleep(1)
60
- self._get_table_rows(SEQUENCE_TABLE)
61
-
62
- for _ in sequence_table.rows:
63
- self.add_table_row(SEQUENCE_TABLE)
64
- self.sleep(1)
65
- self.send(Command.SAVE_SEQUENCE_CMD)
66
- self._get_table_rows(SEQUENCE_TABLE)
67
- self.send(Command.SAVE_SEQUENCE_CMD)
68
- self.send(Command.SWITCH_SEQUENCE_CMD)
69
-
70
- for i, row in enumerate(sequence_table.rows):
71
- self.edit_row(row=row, row_num=i + 1)
72
- self.sleep(1)
73
- self.send(Command.SAVE_SEQUENCE_CMD)
74
76
 
77
+ rows = self.get_num_rows()
78
+ if rows.is_ok():
79
+ existing_row_num = rows.value.num_response
80
+ wanted_row_num = len(sequence_table.rows)
81
+ while existing_row_num != wanted_row_num:
82
+ if wanted_row_num > existing_row_num:
83
+ self.add_row()
84
+ elif wanted_row_num < existing_row_num:
85
+ self.delete_row(int(existing_row_num))
86
+ self.send(Command.SAVE_SEQUENCE_CMD)
87
+ existing_row_num = self.get_num_rows().ok_value.num_response
88
+ self.send(Command.SWITCH_SEQUENCE_CMD)
89
+
90
+ for i, row in enumerate(sequence_table.rows):
91
+ self.edit_row(row=row, row_num=i + 1)
92
+ self.sleep(1)
93
+ self.send(Command.SAVE_SEQUENCE_CMD)
94
+ self.send(Command.SWITCH_SEQUENCE_CMD)
75
95
 
76
96
  def edit_row(self, row: SequenceEntry, row_num: int):
77
97
  """
78
- Edits a row in the sequence table. Assumes the row already exists.
98
+ Edits a row in the sequence table. If a row does NOT exist, a new one will be created.
79
99
 
80
100
  :param row: sequence row entry with updated information
81
- :param row_num: the row to edit, based on -1-based indexing
101
+ :param row_num: the row to edit, based on 1-based indexing
82
102
  """
103
+ num_rows = self.get_num_rows()
104
+ if num_rows.is_ok():
105
+ while num_rows.ok_value.num_response < row_num:
106
+ self.add_row()
107
+ self.send(Command.SAVE_SEQUENCE_CMD)
108
+ num_rows = self.get_num_rows()
109
+
110
+ table_register = self.table.register
111
+ table_name = self.table.name
112
+
83
113
  if row.vial_location:
84
- self.sleepy_send(TableOperation.EDIT_ROW_VAL.value.format(register=SEQUENCE_TABLE.register,
85
- table_name=SEQUENCE_TABLE.name,
114
+ loc = row.vial_location
115
+ if isinstance(row.vial_location, InjectionSource):
116
+ loc = row.vial_location.value
117
+ self.sleepy_send(TableOperation.EDIT_ROW_VAL.value.format(register=table_register,
118
+ table_name=table_name,
86
119
  row=row_num,
87
120
  col_name=RegisterFlag.VIAL_LOCATION,
88
- val=row.vial_location))
121
+ val=loc))
89
122
  if row.method:
90
- self.sleepy_send(TableOperation.EDIT_ROW_TEXT.value.format(register=SEQUENCE_TABLE.register,
91
- table_name=SEQUENCE_TABLE.name,
123
+ possible_path = os.path.join(self.method_dir, row.method) + ".M\\"
124
+ method = row.method
125
+ if os.path.exists(possible_path):
126
+ method = os.path.join(self.method_dir, row.method)
127
+ self.sleepy_send(TableOperation.EDIT_ROW_TEXT.value.format(register=table_register,
128
+ table_name=table_name,
92
129
  row=row_num,
93
130
  col_name=RegisterFlag.METHOD,
94
- val=row.method))
131
+ val=method))
95
132
 
96
133
  if row.num_inj:
97
- self.sleepy_send(TableOperation.EDIT_ROW_VAL.value.format(register=SEQUENCE_TABLE.register,
98
- table_name=SEQUENCE_TABLE.name,
134
+ self.sleepy_send(TableOperation.EDIT_ROW_VAL.value.format(register=table_register,
135
+ table_name=table_name,
99
136
  row=row_num,
100
137
  col_name=RegisterFlag.NUM_INJ,
101
138
  val=row.num_inj))
102
139
 
103
140
  if row.inj_vol:
104
- self.sleepy_send(TableOperation.EDIT_ROW_TEXT.value.format(register=SEQUENCE_TABLE.register,
105
- table_name=SEQUENCE_TABLE.name,
141
+ self.sleepy_send(TableOperation.EDIT_ROW_TEXT.value.format(register=table_register,
142
+ table_name=table_name,
106
143
  row=row_num,
107
144
  col_name=RegisterFlag.INJ_VOL,
108
145
  val=row.inj_vol))
109
146
 
110
147
  if row.inj_source:
111
- self.sleepy_send(TableOperation.EDIT_ROW_TEXT.value.format(register=SEQUENCE_TABLE.register,
112
- table_name=SEQUENCE_TABLE.name,
148
+ self.sleepy_send(TableOperation.EDIT_ROW_TEXT.value.format(register=table_register,
149
+ table_name=table_name,
113
150
  row=row_num,
114
151
  col_name=RegisterFlag.INJ_SOR,
115
152
  val=row.inj_source.value))
116
153
 
117
154
  if row.sample_name:
118
- self.sleepy_send(TableOperation.EDIT_ROW_TEXT.value.format(register=SEQUENCE_TABLE.register,
119
- table_name=SEQUENCE_TABLE.name,
155
+ self.sleepy_send(TableOperation.EDIT_ROW_TEXT.value.format(register=table_register,
156
+ table_name=table_name,
120
157
  row=row_num,
121
158
  col_name=RegisterFlag.NAME,
122
159
  val=row.sample_name))
123
- self.sleepy_send(TableOperation.EDIT_ROW_TEXT.value.format(register=SEQUENCE_TABLE.register,
124
- table_name=SEQUENCE_TABLE.name,
160
+ self.sleepy_send(TableOperation.EDIT_ROW_TEXT.value.format(register=table_register,
161
+ table_name=table_name,
125
162
  row=row_num,
126
163
  col_name=RegisterFlag.DATA_FILE,
127
164
  val=row.sample_name))
128
165
  if row.sample_type:
129
- self.sleepy_send(TableOperation.EDIT_ROW_VAL.value.format(register=SEQUENCE_TABLE.register,
130
- table_name=SEQUENCE_TABLE.name,
166
+ self.sleepy_send(TableOperation.EDIT_ROW_VAL.value.format(register=table_register,
167
+ table_name=table_name,
131
168
  row=row_num,
132
169
  col_name=RegisterFlag.SAMPLE_TYPE,
133
170
  val=row.sample_type.value))
134
171
 
135
- def run(self, sequence_table: SequenceTable):
172
+ self.send(Command.SAVE_SEQUENCE_CMD)
173
+
174
+ def run(self):
136
175
  """
137
176
  Starts the currently loaded sequence, storing data
138
177
  under the <data_dir>/<sequence table name> folder.
139
178
  Device must be ready.
140
-
141
- :param sequence_table:
142
179
  """
143
180
  timestamp = time.strftime(SEQUENCE_TIME_FORMAT)
181
+ seq_table = self.load()
144
182
  self.send(Command.RUN_SEQUENCE_CMD.value)
145
- folder_name = f"{sequence_table.name} {timestamp}"
146
- subdirs = [x[0] for x in os.walk(self.data_dir)]
147
- time.sleep(10)
148
- potential_folders = sorted(list(filter(lambda d: folder_name in d, subdirs)))
149
- parent_folder = potential_folders[0]
150
- self.data_files.append(SequenceDataFiles(
151
- sequence_name=sequence_table.name,
152
- dir=parent_folder,
153
- child_dirs=[r.sample_name + ".D" for r in sequence_table.rows]))
183
+
184
+ if self.check_hplc_is_running():
185
+ folder_name = f"{seq_table.name} {timestamp}"
186
+ self.data_files.append(SequenceDataFiles(dir=folder_name,
187
+ sequence_name=seq_table.name))
188
+
189
+ run_completed = self.check_hplc_done_running(sequence=seq_table)
190
+
191
+ if run_completed.is_ok():
192
+ self.data_files[-1].dir = run_completed.value
193
+ else:
194
+ raise RuntimeError("Run error has occured.")
154
195
 
155
196
  def retrieve_recent_data_files(self):
156
197
  sequence_data_files: SequenceDataFiles = self.data_files[-1]
157
- return os.path.join(sequence_data_files.dir, sequence_data_files.child_dirs[-1])
198
+ return sequence_data_files.dir
199
+
200
+ def get_data(self) -> list[dict[str, AgilentHPLCChromatogram]]:
201
+ parent_dir = self.data_files[-1].dir
202
+ subdirs = [x[0] for x in os.walk(self.data_dir)]
203
+ potential_folders = sorted(list(filter(lambda d: parent_dir in d, subdirs)))
204
+ self.data_files[-1].child_dirs = [f for f in potential_folders if parent_dir in f and ".M" not in f and ".D" in f]
158
205
 
159
- def get_data(self) -> tuple[bool, Any]:
160
- data_ready = self.data_ready()
161
- sequence_data_files: SequenceDataFiles = self.data_files[-1]
162
206
  spectra: list[dict[str, AgilentHPLCChromatogram]] = []
163
- if data_ready:
164
- for row in sequence_data_files.child_dirs:
165
- data_path = os.path.join(sequence_data_files.dir, row)
166
- self.get_spectrum(data_path)
167
- spectra.append(deepcopy(self.spectra))
168
- return data_ready, spectra
169
- else:
170
- return False, None
207
+ for row in self.data_files[-1].child_dirs:
208
+ self.get_spectrum(row)
209
+ spectra.append(deepcopy(self.spectra))
210
+ return spectra
@@ -6,22 +6,26 @@ Authors: Lucy Hao
6
6
 
7
7
  import abc
8
8
  import os
9
+ import time
9
10
  from typing import Union, Optional
10
11
 
11
12
  import polling
13
+ from result import Result, Ok, Err
12
14
 
13
15
  from ...control import CommunicationController
14
16
  from ...utils.chromatogram import AgilentHPLCChromatogram
15
- from ...utils.macro import Command
17
+ from ...utils.macro import Command, HPLCRunningStatus, Response
16
18
  from ...utils.method_types import MethodTimetable
17
- from ...utils.sequence_types import SequenceDataFiles
19
+ from ...utils.sequence_types import SequenceDataFiles, SequenceTable
18
20
  from ...utils.table_types import Table, TableOperation, RegisterFlag
19
21
 
20
22
 
21
23
  class TableController(abc.ABC):
22
24
 
23
- def __init__(self, controller: CommunicationController, src: str, data_dir: str):
25
+ def __init__(self, controller: CommunicationController, src: str, data_dir: str, table: Table):
24
26
  self.controller = controller
27
+ self.table = table
28
+
25
29
  if os.path.isdir(src):
26
30
  self.src: str = src
27
31
  else:
@@ -32,7 +36,7 @@ class TableController(abc.ABC):
32
36
  else:
33
37
  raise FileNotFoundError(f"dir: {data_dir} not found.")
34
38
 
35
- self.spectra = {
39
+ self.spectra: dict[str, AgilentHPLCChromatogram] = {
36
40
  "A": AgilentHPLCChromatogram(self.data_dir),
37
41
  "B": AgilentHPLCChromatogram(self.data_dir),
38
42
  "C": AgilentHPLCChromatogram(self.data_dir),
@@ -41,8 +45,13 @@ class TableController(abc.ABC):
41
45
 
42
46
  self.data_files: Union[list[SequenceDataFiles], list[str]] = []
43
47
 
44
- def receive(self):
45
- return self.controller.receive()
48
+ def receive(self) -> Result[Response, str]:
49
+ for _ in range(10):
50
+ try:
51
+ return self.controller.receive()
52
+ except IndexError:
53
+ continue
54
+ return Err("Could not parse response")
46
55
 
47
56
  def send(self, cmd: Union[Command, str]):
48
57
  self.controller.send(cmd)
@@ -58,7 +67,28 @@ class TableController(abc.ABC):
58
67
  """
59
68
  self.send(Command.SLEEP_CMD.value.format(seconds=seconds))
60
69
 
61
- def add_table_row(self, table: Table):
70
+ def get_num(self, row: int, col_name: RegisterFlag) -> float:
71
+ return self.controller.get_num_val(TableOperation.GET_ROW_VAL.value.format(register=self.table.register,
72
+ table_name=self.table.name,
73
+ row=row,
74
+ col_name=col_name.value))
75
+
76
+ def get_text(self, row: int, col_name: RegisterFlag) -> str:
77
+ return self.controller.get_text_val(TableOperation.GET_ROW_TEXT.value.format(register=self.table.register,
78
+ table_name=self.table.name,
79
+ row=row,
80
+ col_name=col_name.value))
81
+
82
+ @abc.abstractmethod
83
+ def get_row(self, row: int):
84
+ pass
85
+
86
+ def delete_row(self, row: int):
87
+ self.sleepy_send(TableOperation.DELETE_ROW.value.format(register=self.table.register,
88
+ table_name=self.table.name,
89
+ row=row))
90
+
91
+ def add_row(self):
62
92
  """
63
93
  Adds a row to the provided table for currently loaded method or sequence.
64
94
  Import either the SEQUENCE_TABLE or METHOD_TIMETABLE from hein_analytical_control.constants.
@@ -66,10 +96,10 @@ class TableController(abc.ABC):
66
96
 
67
97
  :param table: the table to add a new row to
68
98
  """
69
- self.sleepy_send(TableOperation.NEW_ROW.value.format(register=table.register,
70
- table_name=table.name))
99
+ self.sleepy_send(TableOperation.NEW_ROW.value.format(register=self.table.register,
100
+ table_name=self.table.name))
71
101
 
72
- def delete_table(self, table: Table):
102
+ def delete_table(self):
73
103
  """
74
104
  Deletes the table for the current loaded method or sequence.
75
105
  Import either the SEQUENCE_TABLE or METHOD_TIMETABLE from hein_analytical_control.constants.
@@ -77,46 +107,80 @@ class TableController(abc.ABC):
77
107
 
78
108
  :param table: the table to delete
79
109
  """
80
- self.sleepy_send(TableOperation.DELETE_TABLE.value.format(register=table.register,
81
- table_name=table.name))
110
+ self.sleepy_send(TableOperation.DELETE_TABLE.value.format(register=self.table.register,
111
+ table_name=self.table.name))
82
112
 
83
- def new_table(self, table: Table):
113
+ def new_table(self):
84
114
  """
85
115
  Creates the table for the currently loaded method or sequence. Import either the SEQUENCE_TABLE or
86
116
  METHOD_TIMETABLE from hein_analytical_control.constants. You can also provide your own table.
87
117
 
88
118
  :param table: the table to create
89
119
  """
90
- self.send(TableOperation.CREATE_TABLE.value.format(register=table.register,
91
- table_name=table.name))
92
-
93
- def _get_table_rows(self, table: Table) -> str:
94
- self.send(TableOperation.GET_OBJ_HDR_VAL.value.format(internal_val="Rows",
95
- register=table.register,
96
- table_name=table.name,
97
- col_name=RegisterFlag.NUM_ROWS, ))
120
+ self.send(TableOperation.CREATE_TABLE.value.format(register=self.table.register,
121
+ table_name=self.table.name))
122
+
123
+ def get_num_rows(self) -> Result[int, str]:
124
+ self.send(TableOperation.GET_NUM_ROWS.value.format(register=self.table.register,
125
+ table_name=self.table.name,
126
+ col_name=RegisterFlag.NUM_ROWS))
127
+ self.send(Command.GET_ROWS_CMD.value.format(register=self.table.register,
128
+ table_name=self.table.name,
129
+ col_name=RegisterFlag.NUM_ROWS))
98
130
  res = self.controller.receive()
99
- self.send("Sleep 1")
100
- self.send('Print Rows')
101
- return res
102
131
 
103
- def check_hplc_ready_with_data(self, method: Optional[MethodTimetable] = None) -> bool:
132
+ if res.is_ok():
133
+ self.send("Sleep 0.1")
134
+ self.send('Print Rows')
135
+ return res
136
+ else:
137
+ return Err("No rows could be read.")
138
+
139
+ def check_hplc_is_running(self) -> bool:
140
+ started_running = polling.poll(
141
+ lambda: isinstance(self.controller.get_status(), HPLCRunningStatus),
142
+ step=30,
143
+ max_tries=10)
144
+ return started_running
145
+
146
+ def check_hplc_done_running(self,
147
+ method: Optional[MethodTimetable] = None,
148
+ sequence: Optional[SequenceTable] = None) -> Result[str, str]:
104
149
  """
105
- Checks if ChemStation has finished writing data and can be read back.
150
+ Checks if ChemStation has finished running and can read data back
106
151
 
107
152
  :param method: if you are running a method and want to read back data, the timeout period will be adjusted to be longer than the method's runtime
108
153
  :return: Return True if data can be read back, else False.
109
154
  """
110
- self.controller.set_status()
111
-
112
155
  timeout = 10 * 60
113
- hplc_run_done = polling.poll(
114
- lambda: self.controller.check_data(self.retrieve_recent_data_files()),
156
+ if method:
157
+ timeout = ((method.first_row.maximum_run_time + 2) * 60)
158
+ if sequence:
159
+ timeout *= len(sequence.rows)
160
+
161
+ most_recent_folder = self.retrieve_recent_data_files()
162
+ finished_run = polling.poll(
163
+ lambda: self.controller.check_if_running(),
115
164
  timeout=timeout,
116
- step=30
165
+ step=60
117
166
  )
118
167
 
119
- return hplc_run_done
168
+ if finished_run:
169
+ if os.path.exists(most_recent_folder):
170
+ return Ok(most_recent_folder)
171
+ else:
172
+ subdirs = [x[0] for x in os.walk(self.data_dir)]
173
+ potential_folders = sorted(list(filter(lambda d: most_recent_folder in d, subdirs)))
174
+ parent_dirs = []
175
+ for folder in potential_folders:
176
+ path = os.path.normpath(folder)
177
+ split_folder = path.split(os.sep)
178
+ if most_recent_folder in split_folder[-1]:
179
+ parent_dirs.append(folder)
180
+ parent_dir = sorted(parent_dirs, reverse=True)[0]
181
+ return Ok(parent_dir)
182
+ else:
183
+ return Err("Run did not complete as expected")
120
184
 
121
185
  @abc.abstractmethod
122
186
  def retrieve_recent_data_files(self):
@@ -132,6 +196,3 @@ class TableController(abc.ABC):
132
196
  """
133
197
  for channel, spec in self.spectra.items():
134
198
  spec.load_spectrum(data_path=data_file, channel=channel)
135
-
136
- def data_ready(self) -> bool:
137
- return self.check_hplc_ready_with_data()
@@ -1,5 +1,12 @@
1
1
  from enum import Enum
2
2
  from typing import Union
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass
7
+ class Response:
8
+ string_response: str
9
+ num_response: Union[int, float]
3
10
 
4
11
 
5
12
  # Commands sent to the Chemstation Macro
@@ -8,6 +15,8 @@ class Command(Enum):
8
15
  def __str__(self):
9
16
  return '%s' % self.value
10
17
 
18
+ GET_NUM_VAL_CMD = "response_num = {cmd}"
19
+ GET_TEXT_VAL_CMD = "response$ = {cmd}"
11
20
  RESET_COUNTER_CMD = "last_cmd_no = 0"
12
21
  GET_STATUS_CMD = "response$ = AcqStatus$"
13
22
  SLEEP_CMD = "Sleep {seconds}"
@@ -19,6 +28,7 @@ class Command(Enum):
19
28
  PUMP_ON_CMD = "PumpAll ON"
20
29
  PUMP_OFF_CMD = "PumpAll OFF"
21
30
  GET_METHOD_CMD = "response$ = _MethFile$"
31
+ GET_ROWS_CMD = 'response_num = TabHdrVal({register}, "{table_name}", "{col_name}")'
22
32
  SWITCH_METHOD_CMD = 'LoadMethod "{method_dir}", "{method_name}.M"'
23
33
  START_METHOD_CMD = "StartMethod"
24
34
  RUN_METHOD_CMD = 'RunMethod "{data_dir}",, "{experiment_name}_{timestamp}"'
@@ -26,7 +36,7 @@ class Command(Enum):
26
36
  UPDATE_METHOD_CMD = 'UpdateMethod'
27
37
  SWITCH_SEQUENCE_CMD = 'LoadSequence _SeqPath$, _SeqFile$'
28
38
  SAVE_SEQUENCE_CMD = 'SaveSequence _SeqPath$, _SeqFile$'
29
- SAVE_METHOD_CMD = 'SaveMethod _MethPath$, _MethFile$, {commit_msg}'
39
+ SAVE_METHOD_CMD = 'SaveMethod _MethPath$, _MethFile$, "{commit_msg}"'
30
40
  GET_SEQUENCE_CMD = 'response$ = _SeqFile$'
31
41
  RUN_SEQUENCE_CMD = 'RunSequence'
32
42
 
@@ -22,7 +22,6 @@ class Param:
22
22
  class HPLCMethodParams:
23
23
  organic_modifier: int
24
24
  flow: float
25
- temperature: float
26
25
  maximum_run_time: int
27
26
 
28
27
 
@@ -30,7 +29,7 @@ class HPLCMethodParams:
30
29
  class TimeTableEntry:
31
30
  start_time: float
32
31
  organic_modifer: float
33
- flow: float
32
+ flow: Optional[float] = None
34
33
 
35
34
 
36
35
  @dataclass
@@ -2,20 +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
5
+ from typing import Optional, Union
6
6
 
7
-
8
- @dataclass
9
- class TrayLocation:
10
- row: str
11
- col: int
7
+ from pychemstation.utils.tray_types import TenColumn
12
8
 
13
9
 
14
10
  @dataclass
15
11
  class SequenceDataFiles:
16
12
  sequence_name: str
17
13
  dir: str
18
- child_dirs: list[str]
14
+ child_dirs: Optional[list[str]] = None
19
15
 
20
16
 
21
17
  class SampleType(Enum):
@@ -24,6 +20,10 @@ class SampleType(Enum):
24
20
  CALIBRATION = 3
25
21
  CONTROL = 4
26
22
 
23
+ @classmethod
24
+ def _missing_(cls, value):
25
+ return cls.SAMPLE
26
+
27
27
 
28
28
  class InjectionSource(Enum):
29
29
  AS_METHOD = "As Method"
@@ -31,11 +31,15 @@ class InjectionSource(Enum):
31
31
  MSD = "MSD"
32
32
  HIP_ALS = "HipAls"
33
33
 
34
+ @classmethod
35
+ def _missing_(cls, value):
36
+ return cls.HIP_ALS
37
+
34
38
 
35
39
  @dataclass
36
40
  class SequenceEntry:
37
41
  sample_name: str
38
- vial_location: int
42
+ vial_location: Union[TenColumn, int]
39
43
  method: Optional[str] = None
40
44
  num_inj: Optional[int] = 1
41
45
  inj_vol: Optional[int] = 2
@@ -9,12 +9,14 @@ class TableOperation(Enum):
9
9
  DELETE_TABLE = 'DelTab {register}, "{table_name}"'
10
10
  CREATE_TABLE = 'NewTab {register}, "{table_name}"'
11
11
  NEW_ROW = 'InsTabRow {register}, "{table_name}"'
12
+ DELETE_ROW = 'DelTabRow {register}, "{table_name}", {row}'
12
13
  EDIT_ROW_VAL = 'SetTabVal "{register}", "{table_name}", {row}, "{col_name}", {val}'
13
14
  EDIT_ROW_TEXT = 'SetTabText "{register}", "{table_name}", {row}, "{col_name}", "{val}"'
14
- GET_ROW_VAL = 'TabVal ("{register}", "{table_name}", {row}, "{col_name}")'
15
- GET_ROW_TEXT = 'TabText ("{register}", "{table_name}", {row}, "{col_name}")'
16
- GET_OBJ_HDR_VAL = '{internal_val} = TabHdrVal({register}, "{table_name}", "{col_name}")'
17
- GET_OBJ_HDR_TEXT = ''
15
+ GET_ROW_VAL = 'TabVal("{register}", "{table_name}", {row}, "{col_name}")'
16
+ GET_ROW_TEXT = 'TabText$("{register}", "{table_name}", {row}, "{col_name}")'
17
+ GET_NUM_ROWS = 'Rows = TabHdrVal({register}, "{table_name}", "{col_name}")'
18
+ GET_OBJ_HDR_VAL = 'ObjHdrVal("{register}", "{register_flag}")'
19
+ GET_OBJ_HDR_TEXT = 'ObjHdrText$("{register}", "{register_flag}")'
18
20
  UPDATE_OBJ_HDR_VAL = 'SetObjHdrVal {register}, {register_flag}, {val}'
19
21
  UPDATE_OBJ_HDR_TEXT = 'SetObjHdrText {register}, {register_flag}, {val}'
20
22
  NEW_COL_TEXT = 'NewColText {register}, "{table_name}", "{col_name}", "{val}"'
@@ -39,6 +41,8 @@ class RegisterFlag(Enum):
39
41
  COLUMN_OVEN_TEMP2 = "TemperatureControl2_Temperature"
40
42
  STOPTIME_MODE = "StopTime_Mode"
41
43
  POSTIME_MODE = "PostTime_Mode"
44
+ TIME = "Time"
45
+ TIMETABLE_SOLVENT_B_COMPOSITION = "SolventCompositionPumpChannel2_Percentage"
42
46
 
43
47
  # for Method Timetable
44
48
  SOLVENT_COMPOSITION = "SolventComposition"
@@ -0,0 +1,14 @@
1
+ from enum import Enum
2
+
3
+
4
+ class TenColumn(Enum):
5
+ ONE = 1
6
+ TWO = 2
7
+ THREE = 3
8
+ FOUR = 4
9
+ FIVE = 5
10
+ SIX = 6
11
+ SEVEN = 7
12
+ EIGHT = 8
13
+ NINE = 9
14
+ TEN = 10
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pychemstation
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: Library to interact with Chemstation software, primarily used in Hein lab
5
- Home-page: https://gitlab.com/heingroup/pychemstation
5
+ Home-page: https://gitlab.com/heingroup/device-api/pychemstation
6
6
  Author: Lucy Hao
7
7
  Author-email: lhao03@student.ubc.ca
8
8
  Classifier: Programming Language :: Python :: 3
@@ -12,6 +12,7 @@ Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
13
  Requires-Dist: polling
14
14
  Requires-Dist: seabreeze
15
+ Requires-Dist: xsdata
15
16
 
16
17
  # Agilent HPLC Macro Control
17
18