pychemstation 0.10.7.dev1__py3-none-any.whl → 0.10.8__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.
@@ -83,7 +83,9 @@ class CSVProcessor(ReportProcessor):
83
83
  if "00" in name and file_extension.lower() == "csv":
84
84
  prefix, _, _ = name.partition("00")
85
85
  return prefix
86
- raise FileNotFoundError("Couldn't find the prefix for CSV")
86
+ raise FileNotFoundError(
87
+ "Couldn't find the prefix for CSV, please make sure the post-run settings generate a CSV."
88
+ )
87
89
 
88
90
  def report_contains(self, labels: List[str], want: List[str]):
89
91
  for label in labels:
@@ -173,5 +173,5 @@ class CommunicationController(ABCCommunicationController):
173
173
  data_dirs[i] = "C:" + data_dir
174
174
  return method_dir, sequence_dir, data_dirs
175
175
  raise ValueError(
176
- f"One of the method: {method_dir}, sequence: {sequence_dir} or data directories: {data_dirs} could not be found, please provide your own."
177
- )
176
+ f"One of the method: {method_dir}, sequence: {sequence_dir} or data directories: {data_dirs} could not be found, please provide your own."
177
+ )
@@ -3,17 +3,18 @@ from __future__ import annotations
3
3
  import os
4
4
  import time
5
5
  import warnings
6
- from typing import List, Optional, Union, Dict, Set
6
+ from typing import Dict, List, Optional, Union, Tuple
7
7
 
8
8
  from result import Err, Ok, Result
9
9
 
10
- from ....analysis.process_report import AgilentReport, ReportType
11
- from ....control.controllers import CommunicationController
12
10
  from pychemstation.analysis.chromatogram import (
13
11
  TIME_FORMAT,
14
12
  AgilentChannelChromatogramData,
15
13
  AgilentHPLCChromatogram,
16
14
  )
15
+
16
+ from ....analysis.process_report import AgilentReport, ReportType
17
+ from ....control.controllers import CommunicationController
17
18
  from ....utils.abc_tables.run import RunController
18
19
  from ....utils.macro import Command
19
20
  from ....utils.method_types import (
@@ -23,7 +24,7 @@ from ....utils.method_types import (
23
24
  PType,
24
25
  TimeTableEntry,
25
26
  )
26
- from ....utils.table_types import RegisterFlag, Table, TableOperation, T
27
+ from ....utils.table_types import RegisterFlag, T, Table, TableOperation
27
28
  from ..devices.injector import InjectorController
28
29
 
29
30
 
@@ -50,9 +51,7 @@ class MethodController(RunController):
50
51
  )
51
52
 
52
53
  def get_current_method_name(self) -> str:
53
- time.sleep(2)
54
- self.send(Command.GET_METHOD_CMD)
55
- time.sleep(2)
54
+ self.sleepy_send(Command.GET_METHOD_CMD)
56
55
  res = self.receive()
57
56
  if res.is_ok():
58
57
  return res.ok_value.string_response
@@ -77,26 +76,22 @@ class MethodController(RunController):
77
76
  raise ValueError("Communication controller is offline!")
78
77
 
79
78
  def get_row(self, row: int) -> TimeTableEntry:
80
- flow = None
81
- om = None
82
-
83
- try:
84
- flow = self.get_num(row, RegisterFlag.TIMETABLE_FLOW)
85
- except RuntimeError:
86
- pass
87
- try:
88
- om = self.get_num(row, RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION)
89
- except RuntimeError:
90
- pass
91
-
92
- if om is None and flow is None:
93
- raise ValueError("Both flow and organic modifier is None")
94
-
95
- return TimeTableEntry(
96
- start_time=self.get_num(row, RegisterFlag.TIME),
97
- organic_modifer=om,
98
- flow=flow,
99
- )
79
+ function = self.get_text(row, RegisterFlag.FUNCTION)
80
+ if function == RegisterFlag.FLOW.value:
81
+ return TimeTableEntry(
82
+ start_time=self.get_num(row, RegisterFlag.TIME),
83
+ organic_modifer=None,
84
+ flow=self.get_num(row, RegisterFlag.TIMETABLE_FLOW),
85
+ )
86
+ if function == RegisterFlag.SOLVENT_COMPOSITION.value:
87
+ return TimeTableEntry(
88
+ start_time=self.get_num(row, RegisterFlag.TIME),
89
+ organic_modifer=self.get_num(
90
+ row, RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION
91
+ ),
92
+ flow=None,
93
+ )
94
+ raise ValueError("Both flow and organic modifier are empty")
100
95
 
101
96
  def get_timetable(self, rows: int):
102
97
  uncoalesced_timetable_rows = [self.get_row(r + 1) for r in range(rows)]
@@ -195,7 +190,6 @@ class MethodController(RunController):
195
190
  method_dir=method_dir, method_name=method_name
196
191
  )
197
192
  )
198
-
199
193
  time.sleep(2)
200
194
  self.send(Command.GET_METHOD_CMD)
201
195
  time.sleep(2)
@@ -219,6 +213,7 @@ class MethodController(RunController):
219
213
  new_stop_time=updated_method.stop_time,
220
214
  new_post_time=updated_method.post_time,
221
215
  )
216
+ self.validate_timetable(updated_method.timetable)
222
217
  self.edit_method_timetable(updated_method.timetable)
223
218
 
224
219
  if save:
@@ -229,6 +224,7 @@ class MethodController(RunController):
229
224
  )
230
225
 
231
226
  def edit_initial_om(self, new_om: Union[int, float]):
227
+ self._validate_organic_modifier(new_om)
232
228
  initial_organic_modifier: Param = Param(
233
229
  val=new_om,
234
230
  chemstation_key=RegisterFlag.SOLVENT_B_COMPOSITION,
@@ -236,13 +232,31 @@ class MethodController(RunController):
236
232
  )
237
233
  self._update_param(initial_organic_modifier)
238
234
 
235
+ def _validate_organic_modifier(self, new_om):
236
+ if not (isinstance(new_om, int) or isinstance(new_om, float)):
237
+ raise ValueError("Organic modifier must be int or float")
238
+ if new_om < 0:
239
+ raise ValueError("Organic modifier must be positive")
240
+ if new_om > 100:
241
+ raise ValueError("Organic modifer must be less than 100.")
242
+
239
243
  def edit_flow(self, new_flow: Union[int, float]):
244
+ self._validate_flow(new_flow)
240
245
  flow: Param = Param(
241
246
  val=new_flow, chemstation_key=RegisterFlag.FLOW, ptype=PType.NUM
242
247
  )
243
248
  self._update_param(flow)
244
249
 
250
+ def _validate_flow(self, new_flow):
251
+ if not (isinstance(new_flow, int) or isinstance(new_flow, float)):
252
+ raise ValueError("Flow must be int or float")
253
+ if new_flow < 0:
254
+ raise ValueError("Flow must be positive")
255
+ if new_flow >= 5.0:
256
+ raise ValueError("Flow must be less than 5.0")
257
+
245
258
  def edit_stop_time(self, new_stop_time: Union[int, float]):
259
+ self.validate_stop_time(new_stop_time)
246
260
  stop_time: Param = Param(
247
261
  val=new_stop_time,
248
262
  chemstation_key=RegisterFlag.MAX_TIME,
@@ -255,7 +269,14 @@ class MethodController(RunController):
255
269
  )
256
270
  self._update_param(stop_time)
257
271
 
272
+ def validate_stop_time(self, new_stop_time):
273
+ if not (isinstance(new_stop_time, int) or isinstance(new_stop_time, float)):
274
+ raise ValueError("Stop time must be int or float")
275
+ if new_stop_time < 0:
276
+ raise ValueError("Stop time must be positive")
277
+
258
278
  def edit_post_time(self, new_post_time: Union[int, float]):
279
+ self.validate_post_time(new_post_time)
259
280
  post_time: Param = Param(
260
281
  val=new_post_time,
261
282
  chemstation_key=RegisterFlag.POST_TIME,
@@ -266,6 +287,12 @@ class MethodController(RunController):
266
287
  )
267
288
  self._update_param(post_time)
268
289
 
290
+ def validate_post_time(self, new_post_time):
291
+ if not (isinstance(new_post_time, int) or isinstance(new_post_time, float)):
292
+ raise ValueError("Post time must be int or float")
293
+ if new_post_time < 0:
294
+ raise ValueError("Post time must be positive")
295
+
269
296
  def update_method_params(
270
297
  self,
271
298
  new_flow: Union[int, float],
@@ -326,59 +353,151 @@ class MethodController(RunController):
326
353
  val=method_param.val,
327
354
  )
328
355
  )
329
- time.sleep(2)
330
356
  self.download()
331
357
 
332
358
  def download(self):
333
- self.send("Sleep 1")
334
359
  self.sleepy_send("DownloadRCMethod PMP1")
335
- self.send("Sleep 1")
336
360
 
337
- def _edit_row(self, row: TimeTableEntry, first_row: bool = False):
338
- if first_row:
339
- if row.organic_modifer:
340
- self.add_row()
341
- self.add_new_col_text(
342
- col_name=RegisterFlag.FUNCTION,
343
- val=RegisterFlag.SOLVENT_COMPOSITION.value,
344
- )
361
+ def _edit_row(
362
+ self,
363
+ row: TimeTableEntry,
364
+ first_row: bool,
365
+ time_added: bool,
366
+ flow_added: bool,
367
+ om_added: bool,
368
+ function_added: bool,
369
+ ) -> Tuple[bool, bool, bool, bool]:
370
+ def add_time():
371
+ nonlocal time_added
372
+ nonlocal first_row
373
+ if not time_added:
345
374
  self.add_new_col_num(col_name=RegisterFlag.TIME, val=row.start_time)
375
+ time_added = True
376
+ elif not first_row:
377
+ self._edit_row_num(col_name=RegisterFlag.TIME, val=row.start_time)
378
+
379
+ def add_flow():
380
+ nonlocal flow_added
381
+ nonlocal function_added
382
+ if not flow_added:
383
+ if not function_added:
384
+ self.add_new_col_text(
385
+ col_name=RegisterFlag.FUNCTION,
386
+ val=RegisterFlag.FLOW.value,
387
+ )
388
+ function_added = True
389
+ else:
390
+ self._edit_row_text(
391
+ col_name=RegisterFlag.FUNCTION,
392
+ val=RegisterFlag.FLOW.value,
393
+ )
346
394
  self.add_new_col_num(
347
- col_name=RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION,
348
- val=row.organic_modifer,
395
+ col_name=RegisterFlag.TIMETABLE_FLOW,
396
+ val=row.flow,
349
397
  )
350
- if row.flow:
351
- self.add_row()
352
- self.get_num_rows()
398
+ flow_added = True
399
+ else:
353
400
  self._edit_row_text(
354
401
  col_name=RegisterFlag.FUNCTION, val=RegisterFlag.FLOW.value
355
402
  )
356
- self.add_new_col_num(col_name=RegisterFlag.TIMETABLE_FLOW, val=row.flow)
357
403
  self._edit_row_num(col_name=RegisterFlag.TIMETABLE_FLOW, val=row.flow)
358
- self.download()
359
- else:
360
- if row.organic_modifer:
361
- self.add_row()
362
- self.get_num_rows()
404
+
405
+ def add_om():
406
+ nonlocal om_added
407
+ nonlocal function_added
408
+ if not om_added:
409
+ if not function_added:
410
+ self.add_new_col_text(
411
+ col_name=RegisterFlag.FUNCTION,
412
+ val=RegisterFlag.SOLVENT_COMPOSITION.value,
413
+ )
414
+ function_added = True
415
+ else:
416
+ self._edit_row_text(
417
+ col_name=RegisterFlag.FUNCTION,
418
+ val=RegisterFlag.SOLVENT_COMPOSITION.value,
419
+ )
420
+ self.add_new_col_num(
421
+ col_name=RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION,
422
+ val=row.organic_modifer,
423
+ )
424
+ om_added = True
425
+ else:
363
426
  self._edit_row_text(
364
427
  col_name=RegisterFlag.FUNCTION,
365
428
  val=RegisterFlag.SOLVENT_COMPOSITION.value,
366
429
  )
367
- self._edit_row_num(col_name=RegisterFlag.TIME, val=row.start_time)
368
430
  self._edit_row_num(
369
431
  col_name=RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION,
370
432
  val=row.organic_modifer,
371
433
  )
372
- self.download()
373
- if row.flow:
374
- self.add_row()
375
- self.get_num_rows()
376
- self._edit_row_text(
377
- col_name=RegisterFlag.FUNCTION, val=RegisterFlag.FLOW.value
378
- )
379
- self._edit_row_num(col_name=RegisterFlag.TIMETABLE_FLOW, val=row.flow)
380
- self._edit_row_num(col_name=RegisterFlag.TIME, val=row.start_time)
381
- self.download()
434
+
435
+ if row.organic_modifer:
436
+ self.add_row()
437
+ add_om()
438
+ add_time()
439
+ if row.flow:
440
+ self.add_row()
441
+ add_flow()
442
+ add_time()
443
+ self.download()
444
+ return time_added, flow_added, om_added, function_added
445
+
446
+ # if first_row:
447
+ # time_added = False
448
+ # flow_row_method: Callable = (
449
+ # self.add_new_col_text
450
+ # if row.flow and not row.organic_modifer
451
+ # else self._edit_row_text
452
+ # )
453
+ # if row.organic_modifer:
454
+ # self.add_row()
455
+ # self.add_new_col_text(
456
+ # col_name=RegisterFlag.FUNCTION,
457
+ # val=RegisterFlag.SOLVENT_COMPOSITION.value,
458
+ # )
459
+ # if not time_added:
460
+ # time_added = True
461
+ # self.add_new_col_num(col_name=RegisterFlag.TIME, val=row.start_time)
462
+ # self.add_new_col_num(
463
+ # col_name=RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION,
464
+ # val=row.organic_modifer,
465
+ # )
466
+ # if row.flow:
467
+ # self.add_row()
468
+ # self.get_num_rows()
469
+ # flow_row_method(
470
+ # col_name=RegisterFlag.FUNCTION, val=RegisterFlag.FLOW.value
471
+ # )
472
+ # if not time_added:
473
+ # time_added = True
474
+ # self.add_new_col_num(col_name=RegisterFlag.TIME, val=row.start_time)
475
+ # self.add_new_col_num(col_name=RegisterFlag.TIMETABLE_FLOW, val=row.flow)
476
+ # self._edit_row_num(col_name=RegisterFlag.TIMETABLE_FLOW, val=row.flow)
477
+ # self.download()
478
+ # else:
479
+ # if row.organic_modifer:
480
+ # self.add_row()
481
+ # self.get_num_rows()
482
+ # self._edit_row_text(
483
+ # col_name=RegisterFlag.FUNCTION,
484
+ # val=RegisterFlag.SOLVENT_COMPOSITION.value,
485
+ # )
486
+ # self._edit_row_num(col_name=RegisterFlag.TIME, val=row.start_time)
487
+ # self._edit_row_num(
488
+ # col_name=RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION,
489
+ # val=row.organic_modifer,
490
+ # )
491
+ # self.download()
492
+ # if row.flow:
493
+ # self.add_row()
494
+ # self.get_num_rows()
495
+ # self._edit_row_text(
496
+ # col_name=RegisterFlag.FUNCTION, val=RegisterFlag.FLOW.value
497
+ # )
498
+ # self._edit_row_num(col_name=RegisterFlag.TIMETABLE_FLOW, val=row.flow)
499
+ # self._edit_row_num(col_name=RegisterFlag.TIME, val=row.start_time)
500
+ # self.download()
382
501
 
383
502
  def edit_method_timetable(self, timetable_rows: List[TimeTableEntry]):
384
503
  self.get_num_rows()
@@ -389,10 +508,23 @@ class MethodController(RunController):
389
508
  res = self.get_num_rows()
390
509
 
391
510
  self.new_table()
392
- self.get_num_rows()
393
-
511
+ num_rows = self.get_num_rows()
512
+ if num_rows.ok_value.num_response != 0:
513
+ raise ValueError("Should be zero rows!")
514
+
515
+ time_added = False
516
+ flow_added = False
517
+ om_added = False
518
+ function_added = False
394
519
  for i, row in enumerate(timetable_rows):
395
- self._edit_row(row=row, first_row=i == 0)
520
+ time_added, flow_added, om_added, function_added = self._edit_row(
521
+ row=row,
522
+ first_row=i == 0,
523
+ time_added=time_added,
524
+ flow_added=flow_added,
525
+ om_added=om_added,
526
+ function_added=function_added,
527
+ )
396
528
 
397
529
  def stop(self):
398
530
  """
@@ -441,11 +573,11 @@ class MethodController(RunController):
441
573
  else:
442
574
  warnings.warn(run_completed.err_value)
443
575
  else:
444
- folder = self._fuzzy_match_most_recent_folder(self.data_files[-1], None)
576
+ folder = self._fuzzy_match_most_recent_folder(self.data_files[-1])
445
577
  i = 0
446
578
  while folder.is_err() and i < 10:
447
- folder = self._fuzzy_match_most_recent_folder(self.data_files[-1], None)
448
- i+=1
579
+ folder = self._fuzzy_match_most_recent_folder(self.data_files[-1])
580
+ i += 1
449
581
  if folder.is_ok():
450
582
  self.data_files[-1] = folder.ok_value
451
583
  else:
@@ -453,11 +585,11 @@ class MethodController(RunController):
453
585
  warnings.warn(warning)
454
586
 
455
587
  def _fuzzy_match_most_recent_folder(
456
- self, most_recent_folder: T, child_dirs: Optional[Set[str]]
588
+ self, most_recent_folder: T
457
589
  ) -> Result[str, str]:
458
590
  if isinstance(most_recent_folder, str) or isinstance(most_recent_folder, bytes):
459
591
  if os.path.exists(most_recent_folder):
460
- return Ok(most_recent_folder)
592
+ return Ok(str(most_recent_folder))
461
593
  return Err("Folder not found!")
462
594
  raise ValueError("Folder is not a str or byte type.")
463
595
 
@@ -490,3 +622,25 @@ class MethodController(RunController):
490
622
  if len(possible_data.x) > 0:
491
623
  signal.data = possible_data
492
624
  return [metd_report]
625
+
626
+ def _validate_row(self, row: TimeTableEntry):
627
+ if not (row.flow or row.organic_modifer):
628
+ raise ValueError(
629
+ "Require one of flow or organic modifier for the method timetable entry!"
630
+ )
631
+ if row.flow:
632
+ self._validate_flow(row.flow)
633
+ if row.organic_modifer:
634
+ self._validate_organic_modifier(row.organic_modifer)
635
+
636
+ def validate_timetable(self, timetable: List[TimeTableEntry]):
637
+ start_time = 0.0
638
+ for i, row in enumerate(timetable):
639
+ if row.start_time > start_time:
640
+ start_time = row.start_time
641
+ elif row.start_time <= start_time:
642
+ raise ValueError(
643
+ f"""Every row's start time must be larger than the previous start time.
644
+ Row {i + 1} ({timetable[i].start_time}) has a smaller or equal starttime than row {i} ({start_time})"""
645
+ )
646
+ self._validate_row(row)
@@ -1,7 +1,9 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
2
4
  import time
3
5
  import warnings
4
- from typing import Any, Dict, List, Optional, Union, Set
6
+ from typing import Any, Dict, List, Optional, Union, Tuple
5
7
 
6
8
  from result import Err, Ok, Result
7
9
  from typing_extensions import override
@@ -9,6 +11,7 @@ from typing_extensions import override
9
11
  from pychemstation.analysis.chromatogram import (
10
12
  AgilentChannelChromatogramData,
11
13
  AgilentHPLCChromatogram,
14
+ SEQUENCE_TIME_FORMAT,
12
15
  )
13
16
 
14
17
  from ....analysis.process_report import AgilentReport, ReportType
@@ -118,7 +121,6 @@ class SequenceController(RunController):
118
121
  parsed_response = self.get_current_sequence_name()
119
122
 
120
123
  assert parsed_response == f"{seq_name}.S", "Switching sequence failed."
121
- self.table_state = self.load()
122
124
 
123
125
  def get_current_sequence_name(self):
124
126
  self.send(Command.GET_SEQUENCE_CMD)
@@ -186,6 +188,8 @@ class SequenceController(RunController):
186
188
  def edit_sample_type(
187
189
  self, sample_type: SampleType, row_num: int, save: bool = True
188
190
  ):
191
+ if not isinstance(sample_type, SampleType):
192
+ raise ValueError("`sample_type` should be of type `SampleType`")
189
193
  self._edit_row_num(
190
194
  row=row_num,
191
195
  col_name=RegisterFlag.SAMPLE_TYPE,
@@ -207,6 +211,8 @@ class SequenceController(RunController):
207
211
  def edit_injection_source(
208
212
  self, inj_source: InjectionSource, row_num: int, save: bool = True
209
213
  ):
214
+ if not isinstance(inj_source, InjectionSource):
215
+ raise ValueError("`inj_source` should be of type `InjectionSource`")
210
216
  self._edit_row_text(
211
217
  row=row_num, col_name=RegisterFlag.INJ_SOR, val=inj_source.value
212
218
  )
@@ -224,25 +230,65 @@ class SequenceController(RunController):
224
230
 
225
231
  def edit_num_injections(self, num_inj: int, row_num: int, save: bool = True):
226
232
  self._edit_row_num(row=row_num, col_name=RegisterFlag.NUM_INJ, val=num_inj)
233
+ if save:
234
+ self.save()
227
235
 
228
- def edit_method_name(self, method: str, row_num: int, save: bool = True):
236
+ def edit_method_name(
237
+ self, method: str, row_num: int, save: bool = True, override_check: bool = False
238
+ ):
229
239
  method_dir = self.method_controller.src
230
240
  possible_path = os.path.join(method_dir, method) + ".M\\"
231
241
  if os.path.exists(possible_path):
232
242
  method = os.path.join(method_dir, method)
243
+ elif not override_check:
244
+ raise ValueError(
245
+ "Method may not exist. If you would still like to use this method, set the `override_check` flag to `True`"
246
+ )
233
247
  self._edit_row_text(row=row_num, col_name=RegisterFlag.METHOD, val=method)
234
248
  if save:
235
249
  self.save()
236
250
 
237
251
  def edit_vial_location(self, loc: Tray, row_num: int, save: bool = True):
238
252
  loc_num = -1
239
- if isinstance(loc, VialBar):
240
- loc_num = loc.value
241
- elif isinstance(loc, FiftyFourVialPlate):
242
- loc_num = loc.value()
243
- self._edit_row_num(
244
- row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc_num
245
- )
253
+ try:
254
+ previous_contents = self.get_row(row_num)
255
+ if (
256
+ isinstance(loc, VialBar)
257
+ and isinstance(previous_contents.vial_location, VialBar)
258
+ or isinstance(loc, FiftyFourVialPlate)
259
+ and isinstance(previous_contents.vial_location, FiftyFourVialPlate)
260
+ ):
261
+ if isinstance(loc, VialBar):
262
+ loc_num = loc.value
263
+ elif isinstance(loc, FiftyFourVialPlate):
264
+ loc_num = loc.value()
265
+ self._edit_row_num(
266
+ row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc_num
267
+ )
268
+ elif isinstance(loc, VialBar) or isinstance(loc, FiftyFourVialPlate):
269
+ self.add_row()
270
+ previous_contents.vial_location = loc
271
+ num_rows = self.get_num_rows().ok_value.num_response
272
+ self._edit_row(previous_contents, num_rows)
273
+ self.move_row(int(num_rows), row_num)
274
+ self.delete_row(row_num + 1)
275
+ self.save()
276
+ else:
277
+ raise ValueError(
278
+ "`loc` should be of type `VialBar`, `FiftyFourVialPlate`"
279
+ )
280
+ except Exception:
281
+ if not (isinstance(loc, VialBar) or isinstance(loc, FiftyFourVialPlate)):
282
+ raise ValueError(
283
+ "`loc` should be of type `VialBar`, `FiftyFourVialPlate`"
284
+ )
285
+ if isinstance(loc, VialBar):
286
+ loc_num = loc.value
287
+ elif isinstance(loc, FiftyFourVialPlate):
288
+ loc_num = loc.value()
289
+ self._edit_row_num(
290
+ row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc_num
291
+ )
246
292
  if save:
247
293
  self.save()
248
294
 
@@ -255,11 +301,6 @@ class SequenceController(RunController):
255
301
  under the <data_dir>/<sequence table name> folder.
256
302
  Device must be ready.
257
303
  """
258
- if self.controller:
259
- self.controller.send(Command.SAVE_METHOD_CMD)
260
- self.controller.send(Command.SAVE_SEQUENCE_CMD)
261
- else:
262
- raise ValueError("Controller is offline!")
263
304
 
264
305
  current_sequence_name = self.get_current_sequence_name()
265
306
  if not self.table_state or self.table_state.name not in current_sequence_name:
@@ -284,6 +325,11 @@ class SequenceController(RunController):
284
325
  curr_method_runtime = self.method_controller.get_total_runtime()
285
326
  total_runtime += curr_method_runtime
286
327
 
328
+ timestamp = time.strftime(SEQUENCE_TIME_FORMAT)
329
+ folder_name = f"{self.table_state.name} {timestamp}"
330
+
331
+ self.send(Command.SAVE_METHOD_CMD)
332
+ self.send(Command.SAVE_SEQUENCE_CMD)
287
333
  self.send(Command.RUN_SEQUENCE_CMD.value)
288
334
  self.timeout = total_runtime * 60
289
335
 
@@ -295,20 +341,11 @@ class SequenceController(RunController):
295
341
  break
296
342
 
297
343
  if hplc_running:
298
- full_path_name, current_sample_file = None, None
299
- for _ in range(5):
300
- try:
301
- full_path_name, current_sample_file = (
302
- self.get_current_run_data_dir_file()
303
- )
304
- break
305
- except ValueError:
306
- pass
344
+ full_path_name, current_sample_file = self.try_getting_run_info(folder_name)
307
345
  if full_path_name and current_sample_file:
308
346
  data_file = SequenceDataFiles(
309
347
  sequence_name=self.table_state.name,
310
348
  dir=full_path_name,
311
- _data_files=[r.data_file for r in self.table_state.rows],
312
349
  child_dirs=[os.path.join(full_path_name, current_sample_file)],
313
350
  )
314
351
  self.data_files.append(data_file)
@@ -324,38 +361,34 @@ class SequenceController(RunController):
324
361
  else:
325
362
  raise RuntimeError("Sequence run may not have started.")
326
363
 
364
+ def try_getting_run_info(self, folder_name: str) -> Tuple[str, str | None]:
365
+ full_path_name, current_sample_file = None, None
366
+ for _ in range(5):
367
+ try:
368
+ full_path_name, current_sample_file = (
369
+ self.get_current_run_data_dir_file()
370
+ )
371
+ except ValueError:
372
+ pass
373
+ if current_sample_file and full_path_name:
374
+ return full_path_name, current_sample_file
375
+ elif full_path_name:
376
+ return full_path_name, None
377
+ raise ValueError("Could not get sequence data folder")
378
+
327
379
  @override
328
380
  def _fuzzy_match_most_recent_folder(
329
- self, most_recent_folder: T, child_dirs: Optional[Set[str]]
381
+ self, most_recent_folder: T
330
382
  ) -> Result[SequenceDataFiles, str]:
331
383
  if isinstance(most_recent_folder, SequenceDataFiles):
332
384
  try:
333
385
  if most_recent_folder.dir and os.path.isdir(most_recent_folder.dir):
334
386
  subdirs = [x[0] for x in os.walk(most_recent_folder.dir)]
335
- if (
336
- child_dirs
337
- and len(child_dirs) > 0
338
- and set(most_recent_folder._data_files) == child_dirs
339
- ):
340
- most_recent_folder.child_dirs = [
341
- os.path.join(most_recent_folder.dir, f) for f in child_dirs
342
- ]
343
- else:
344
- potential_folders: List[str] = sorted(
345
- list(
346
- filter(
347
- lambda d: most_recent_folder.dir in d,
348
- subdirs,
349
- )
350
- )
351
- )
352
- most_recent_folder.child_dirs = [
353
- f
354
- for f in potential_folders
355
- if most_recent_folder.dir in f
356
- and ".M" not in f
357
- and ".D" in f
358
- ]
387
+ most_recent_folder.child_dirs = [
388
+ f
389
+ for f in subdirs
390
+ if most_recent_folder.dir in f and ".D" in f and f[-1] == "D"
391
+ ]
359
392
  return Ok(most_recent_folder)
360
393
  else:
361
394
  return Err("No sequence folder found, please give the full path.")
@@ -370,14 +403,11 @@ class SequenceController(RunController):
370
403
  if custom_path
371
404
  else self.data_files[-1]
372
405
  )
373
- if len(seq_data_dir.child_dirs) == 0:
374
- search_folder = self._fuzzy_match_most_recent_folder(
375
- seq_data_dir, set(seq_data_dir.child_dirs)
376
- )
377
- if search_folder.is_ok():
378
- seq_data_dir = search_folder.ok_value
379
- else:
380
- raise FileNotFoundError(search_folder.err_value)
406
+ search_folder = self._fuzzy_match_most_recent_folder(seq_data_dir)
407
+ if search_folder.is_ok():
408
+ seq_data_dir = search_folder.ok_value
409
+ else:
410
+ raise FileNotFoundError(search_folder.err_value)
381
411
  all_w_spectra: List[Dict[int, AgilentHPLCChromatogram]] = []
382
412
  for row in seq_data_dir.child_dirs:
383
413
  all_w_spectra.append(self.get_data_uv(custom_path=row))
@@ -401,10 +431,9 @@ class SequenceController(RunController):
401
431
  if custom_path
402
432
  else self.data_files[-1]
403
433
  )
404
- if len(seq_file_dir.child_dirs) == 0:
405
- self.data_files[-1] = self._fuzzy_match_most_recent_folder(
406
- seq_file_dir, set(seq_file_dir.child_dirs)
407
- ).ok_value
434
+ self.data_files[-1] = self._fuzzy_match_most_recent_folder(
435
+ seq_file_dir
436
+ ).ok_value
408
437
  spectra: List[AgilentChannelChromatogramData] = []
409
438
  for row in self.data_files[-1].child_dirs:
410
439
  self.get_spectrum_at_channels(row)
@@ -423,10 +452,13 @@ class SequenceController(RunController):
423
452
  dir=custom_path,
424
453
  sequence_name="NA",
425
454
  ),
426
- child_dirs=None,
427
455
  ).ok_value
428
456
  )
429
457
  parent_dir = self.data_files[-1]
458
+ parent_dir = self._fuzzy_match_most_recent_folder(
459
+ most_recent_folder=parent_dir,
460
+ ).ok_value
461
+ assert len(parent_dir.child_dirs) != 0
430
462
  spectra = self.get_data()
431
463
  reports = []
432
464
  for i, child_dir in enumerate(parent_dir.child_dirs):
@@ -20,7 +20,7 @@ from ..control.controllers import CommunicationController
20
20
  from ..utils.injector_types import InjectorTable
21
21
  from ..utils.macro import Command, Response, Status
22
22
  from ..utils.method_types import MethodDetails
23
- from ..utils.sequence_types import SequenceTable
23
+ from ..utils.sequence_types import SequenceTable, SequenceDataFiles
24
24
  from ..utils.table_types import Table
25
25
 
26
26
 
@@ -107,7 +107,11 @@ class HPLCController:
107
107
  raise RuntimeError(
108
108
  "Communication controller must be initialized before sending command. It is currently in offline mode."
109
109
  )
110
- return self.comm.receive().value
110
+ res = self.comm.receive()
111
+ if res.is_ok():
112
+ return res.ok_value
113
+ else:
114
+ return res.err_value
111
115
 
112
116
  def status(self) -> Status:
113
117
  """Get the current status of the HPLC machine.
@@ -202,6 +206,16 @@ class HPLCController:
202
206
  """
203
207
  self.sequence_controller.edit(updated_sequence)
204
208
 
209
+ def get_last_run_method_file_path(self) -> str:
210
+ """Get the folder (ending with .D) for last run method.
211
+
212
+ :return: Complete path for method run.
213
+ """
214
+ if len(self.method_controller.data_files) > 0:
215
+ return self.method_controller.data_files[-1]
216
+ else:
217
+ raise UserWarning("No data yet!")
218
+
205
219
  def get_last_run_method_report(
206
220
  self,
207
221
  custom_path: Optional[str] = None,
@@ -230,6 +244,19 @@ class HPLCController:
230
244
  else:
231
245
  return self.method_controller.get_data(custom_path=custom_path)
232
246
 
247
+ def get_last_run_sequence_file_paths(self) -> SequenceDataFiles:
248
+ """Get the sequence folder and all run folders (ending with .D).
249
+
250
+ :return: `SequenceDataFiles` containing complete path locations for sequence folder and all runs.
251
+ """
252
+ if len(self.sequence_controller.data_files):
253
+ self.sequence_controller._fuzzy_match_most_recent_folder(
254
+ most_recent_folder=self.sequence_controller.data_files[-1],
255
+ )
256
+ return self.sequence_controller.data_files[-1]
257
+ else:
258
+ raise UserWarning("No data files yet!")
259
+
233
260
  def get_last_run_sequence_reports(
234
261
  self,
235
262
  custom_path: Optional[str] = None,
@@ -57,7 +57,6 @@ class ABCCommunicationController(abc.ABC):
57
57
 
58
58
  self.reset_cmd_counter()
59
59
  self._most_recent_hplc_status: Status = self.get_status()
60
- self.send("Local Rows")
61
60
 
62
61
  @abstractmethod
63
62
  def get_num_val(self, cmd: str) -> Union[int, float]:
@@ -89,15 +88,19 @@ class ABCCommunicationController(abc.ABC):
89
88
  :return: whether the HPLC machine is in a safe state to retrieve data back."""
90
89
  self.set_status()
91
90
  hplc_avail = isinstance(self._most_recent_hplc_status, HPLCAvailStatus)
92
- time.sleep(5)
91
+ time.sleep(10)
93
92
  self.set_status()
94
93
  hplc_actually_avail = isinstance(self._most_recent_hplc_status, HPLCAvailStatus)
95
- return hplc_avail and hplc_actually_avail
94
+ time.sleep(10)
95
+ self.set_status()
96
+ hplc_final_check_avail = isinstance(
97
+ self._most_recent_hplc_status, HPLCAvailStatus
98
+ )
99
+ return hplc_avail and hplc_actually_avail and hplc_final_check_avail
96
100
 
97
101
  def sleepy_send(self, cmd: Union[Command, str]):
98
102
  self.send("Sleep 0.1")
99
103
  self.send(cmd)
100
- self.send("Sleep 0.1")
101
104
 
102
105
  def send(self, cmd: Union[Command, str]):
103
106
  """Sends a command to Chemstation.
@@ -94,9 +94,7 @@ class RunController(ABCTableController, abc.ABC):
94
94
  return object.__new__(cls)
95
95
 
96
96
  @abc.abstractmethod
97
- def _fuzzy_match_most_recent_folder(
98
- self, most_recent_folder: T, child_dirs: Set[str]
99
- ) -> Result[T, str]:
97
+ def _fuzzy_match_most_recent_folder(self, most_recent_folder: T) -> Result[T, str]:
100
98
  pass
101
99
 
102
100
  @abc.abstractmethod
@@ -192,9 +190,7 @@ class RunController(ABCTableController, abc.ABC):
192
190
  else:
193
191
  raise ValueError("Timeout value is None, no comparison can be made.")
194
192
 
195
- check_folder = self._fuzzy_match_most_recent_folder(
196
- self.data_files[-1], self.current_run_child_files
197
- )
193
+ check_folder = self._fuzzy_match_most_recent_folder(self.data_files[-1])
198
194
  if check_folder.is_ok() and finished_run:
199
195
  return check_folder
200
196
  elif check_folder.is_ok():
@@ -255,9 +251,14 @@ class RunController(ABCTableController, abc.ABC):
255
251
  self.send(Command.GET_CURRENT_RUN_DATA_FILE)
256
252
  current_sample_file = self.receive()
257
253
  if full_path_name.is_ok() and current_sample_file.is_ok():
258
- return (
259
- full_path_name.ok_value.string_response,
260
- current_sample_file.ok_value.string_response,
261
- )
262
- else:
263
- raise ValueError("Couldn't read data dir and file.")
254
+ if os.path.isdir(full_path_name.ok_value.string_response) and os.path.isdir(
255
+ os.path.join(
256
+ full_path_name.ok_value.string_response,
257
+ current_sample_file.ok_value.string_response,
258
+ )
259
+ ):
260
+ return (
261
+ full_path_name.ok_value.string_response,
262
+ current_sample_file.ok_value.string_response,
263
+ )
264
+ raise ValueError("Couldn't read data dir and file or doesn't exist yet.")
@@ -126,8 +126,8 @@ class ABCTableController(abc.ABC):
126
126
  ):
127
127
  if not (isinstance(val, int) or isinstance(val, float)):
128
128
  raise ValueError(f"{val} must be an int or float.")
129
+ num_rows = self.get_num_rows()
129
130
  if row:
130
- num_rows = self.get_num_rows()
131
131
  if num_rows.is_ok():
132
132
  if num_rows.ok_value.num_response < row:
133
133
  raise ValueError("Not enough rows to edit!")
@@ -136,7 +136,7 @@ class ABCTableController(abc.ABC):
136
136
  TableOperation.EDIT_ROW_VAL.value.format(
137
137
  register=self.table_locator.register,
138
138
  table_name=self.table_locator.name,
139
- row=row if row is not None else "Rows",
139
+ row=row if row is not None else "response_num",
140
140
  col_name=col_name,
141
141
  val=val,
142
142
  )
@@ -147,8 +147,8 @@ class ABCTableController(abc.ABC):
147
147
  ):
148
148
  if not isinstance(val, str):
149
149
  raise ValueError(f"{val} must be a str.")
150
+ num_rows = self.get_num_rows()
150
151
  if row:
151
- num_rows = self.get_num_rows()
152
152
  if num_rows.is_ok():
153
153
  if num_rows.ok_value.num_response < row:
154
154
  raise ValueError("Not enough rows to edit!")
@@ -157,7 +157,7 @@ class ABCTableController(abc.ABC):
157
157
  TableOperation.EDIT_ROW_TEXT.value.format(
158
158
  register=self.table_locator.register,
159
159
  table_name=self.table_locator.name,
160
- row=row if row is not None else "Rows",
160
+ row=row if row is not None else "response_num",
161
161
  col_name=col_name,
162
162
  val=val,
163
163
  )
@@ -178,11 +178,15 @@ class ABCTableController(abc.ABC):
178
178
 
179
179
  def add_row(self):
180
180
  """Adds a row to the provided table for currently loaded method or sequence."""
181
+ previous_row_count = self.get_num_rows().ok_value.num_response
181
182
  self.sleepy_send(
182
183
  TableOperation.NEW_ROW.value.format(
183
184
  register=self.table_locator.register, table_name=self.table_locator.name
184
185
  )
185
186
  )
187
+ new_row_count = self.get_num_rows().ok_value.num_response
188
+ if previous_row_count + 1 != new_row_count:
189
+ raise ValueError("Row could not be added.")
186
190
 
187
191
  def delete_table(self):
188
192
  """Deletes the table."""
@@ -201,13 +205,6 @@ class ABCTableController(abc.ABC):
201
205
  )
202
206
 
203
207
  def get_num_rows(self) -> Result[Response, str]:
204
- self.send(
205
- TableOperation.GET_NUM_ROWS.value.format(
206
- register=self.table_locator.register,
207
- table_name=self.table_locator.name,
208
- col_name=RegisterFlag.NUM_ROWS,
209
- )
210
- )
211
208
  self.send(
212
209
  Command.GET_ROWS_CMD.value.format(
213
210
  register=self.table_locator.register,
@@ -221,8 +218,16 @@ class ABCTableController(abc.ABC):
221
218
  raise ValueError("Controller is offline")
222
219
 
223
220
  if res.is_ok():
224
- self.send("Sleep 0.1")
225
- self.send("Print Rows")
226
221
  return res
227
222
  else:
228
223
  return Err("No rows could be read.")
224
+
225
+ def move_row(self, from_row: int, to_row: int):
226
+ self.send(
227
+ TableOperation.MOVE_ROW.value.format(
228
+ register=self.table_locator.register,
229
+ table_name=self.table_locator.name,
230
+ from_row=from_row,
231
+ to_row=to_row,
232
+ )
233
+ )
@@ -55,12 +55,15 @@ class Command(Enum):
55
55
  # Get directories
56
56
  GET_METHOD_DIR = "response$ = _METHPATH$"
57
57
  GET_SEQUENCE_DIR = "response$ = _SEQUENCEPATHS$"
58
+ CONFIG_MET_PATH = "_CONFIGMETPATH"
59
+ CONFIG_SEQ_PATH = "_CONFIGSEQPATH"
60
+ GET_RUNNING_SEQUENCE_DIR = "response$ = _SEQPATHS$"
58
61
  GET_DATA_DIRS = "response$ = _DATAPATHS$"
59
62
  GET_CURRENT_RUN_DATA_DIR = "response$ = _DATAPath$"
60
63
  GET_CURRENT_RUN_DATA_FILE = "response$ = _DATAFILE1$"
61
64
 
62
- # Debuggng
63
- ERROR = "response$ = _ERROR$"
65
+ # Errors
66
+ ERROR = "response$ = _ERRMSG$"
64
67
 
65
68
 
66
69
  class HPLCRunningStatus(Enum):
@@ -33,7 +33,7 @@ class TimeTableEntry:
33
33
  """Row in a method timetable."""
34
34
 
35
35
  start_time: float
36
- organic_modifer: Optional[float]
36
+ organic_modifer: Optional[float] = None
37
37
  flow: Optional[float] = None
38
38
 
39
39
 
@@ -1,5 +1,28 @@
1
- from ...control.controllers.abc_tables.abc_comm import ABCCommunicationController
1
+ from typing import Union
2
+
3
+ from result import Result
4
+
5
+ from .mock_hplc import MockHPLC
6
+ from ..abc_tables.abc_comm import ABCCommunicationController
7
+ from ..macro import Status
2
8
 
3
9
 
4
10
  class MockCommunicationController(ABCCommunicationController):
5
- pass
11
+ def __init__(self, comm_dir: str):
12
+ super().__init__(comm_dir)
13
+ self.hplc = MockHPLC()
14
+
15
+ def get_num_val(self, cmd: str) -> Union[int, float]:
16
+ raise NotImplementedError
17
+
18
+ def get_text_val(self, cmd: str) -> str:
19
+ raise NotImplementedError
20
+
21
+ def get_status(self) -> Status:
22
+ raise NotImplementedError
23
+
24
+ def _send(self, cmd: str, cmd_no: int, num_attempts=5) -> None:
25
+ raise NotImplementedError
26
+
27
+ def _receive(self, cmd_no: int, num_attempts=100) -> Result[str, str]:
28
+ raise NotImplementedError
@@ -1,2 +1,30 @@
1
+ from ..macro import HPLCAvailStatus, Status
2
+ from ..method_types import MethodDetails, HPLCMethodParams, TimeTableEntry
3
+ from ..sequence_types import SequenceTable, SequenceEntry, InjectionSource
4
+ from ..tray_types import FiftyFourVialPlate
5
+
6
+
1
7
  class MockHPLC:
2
- pass
8
+ def __init__(self):
9
+ self.current_method: MethodDetails = MethodDetails(
10
+ name="General-Poroshell",
11
+ params=HPLCMethodParams(organic_modifier=5, flow=0.65),
12
+ timetable=[TimeTableEntry(start_time=3, organic_modifer=99, flow=0.65)],
13
+ stop_time=5,
14
+ post_time=2,
15
+ )
16
+ self.current_sequence: SequenceTable = SequenceTable(
17
+ name="hplc_testing",
18
+ rows=[
19
+ SequenceEntry(
20
+ vial_location=FiftyFourVialPlate.from_str("P1-A2"),
21
+ sample_name="sample1",
22
+ data_file="sample1",
23
+ method="General-Poroshell",
24
+ num_inj=1,
25
+ inj_vol=1,
26
+ inj_source=InjectionSource.HIP_ALS,
27
+ )
28
+ ],
29
+ )
30
+ self.current_status: Status = HPLCAvailStatus.STANDBY
@@ -13,13 +13,11 @@ class SequenceDataFiles:
13
13
 
14
14
  :param sequence_name: the name of the sequence that is running
15
15
  :param dir: the complete path of the directory generated for the sequence
16
- :param _data_files: the names of the files of the sequence runs, ends with ".D"
17
16
  :param child_dirs: the complete path of the files for sequence runs, contains the Chemstation data, `dir` and the sample run file.
18
17
  """
19
18
 
20
19
  sequence_name: str
21
20
  dir: str
22
- _data_files: List[str] = field(default_factory=list)
23
21
  child_dirs: List[str] = field(default_factory=list)
24
22
 
25
23
 
@@ -78,10 +76,11 @@ class SequenceTable:
78
76
  for self_row, other_row in zip(self.rows, other.rows):
79
77
  equal &= self_row.vial_location == other_row.vial_location
80
78
  equal &= self_row.data_file == other_row.data_file
81
- equal &= (
82
- os.path.split(os.path.normpath(self_row.method))[-1]
83
- == os.path.split(os.path.normpath(other_row.method))[-1]
84
- )
79
+ if self_row.method and other_row.method:
80
+ equal &= (
81
+ os.path.split(os.path.normpath(self_row.method))[-1]
82
+ == os.path.split(os.path.normpath(other_row.method))[-1]
83
+ )
85
84
  equal &= self_row.num_inj == other_row.num_inj
86
85
  equal &= self_row.inj_vol == other_row.inj_vol
87
86
  equal &= self_row.inj_source == other_row.inj_source
@@ -16,6 +16,8 @@ class TableOperation(Enum):
16
16
  DELETE_TABLE = 'DelTab {register}, "{table_name}"'
17
17
  CREATE_TABLE = 'NewTab {register}, "{table_name}"'
18
18
  NEW_ROW = 'InsTabRow {register}, "{table_name}"'
19
+ NEW_ROW_SPECIFIC = 'InsTabRow {register}, "{table_name}"'
20
+ MOVE_ROW = 'MoveTabRow {register}, "{table_name}", {from_row}, {to_row}'
19
21
  DELETE_ROW = 'DelTabRow {register}, "{table_name}", {row}'
20
22
  EDIT_ROW_VAL = 'SetTabVal "{register}", "{table_name}", {row}, "{col_name}", {val}'
21
23
  EDIT_ROW_TEXT = (
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pychemstation
3
- Version: 0.10.7.dev1
3
+ Version: 0.10.8
4
4
  Summary: Library to interact with Chemstation software, primarily used in Hein lab
5
5
  Project-URL: Documentation, https://pychemstation-e5a086.gitlab.io/pychemstation.html
6
6
  Project-URL: Repository, https://gitlab.com/heingroup/device-api/pychemstation
@@ -2,16 +2,16 @@ pychemstation/__init__.py,sha256=Sc4z8LRVFMwJUoc_DPVUriSXTZ6PO9MaJ80PhRbKyB8,34
2
2
  pychemstation/analysis/__init__.py,sha256=mPNnp0TmkoUxrTGcT6wNKMyCiOar5vC0cTPmFLrDU1Q,313
3
3
  pychemstation/analysis/base_spectrum.py,sha256=HjbTPoueR7XB1_puCcmx7zbQmeT5SH6_HVwHrYhNXhA,16537
4
4
  pychemstation/analysis/chromatogram.py,sha256=Jk6xOMHA6kSV597fJDZgrFRlvVorldvK4zerPASZrug,3969
5
- pychemstation/analysis/process_report.py,sha256=aTsW6u5-0iwsH3jQtkoKE9jbsy5NAUG6l7O-I49B8kY,14650
5
+ pychemstation/analysis/process_report.py,sha256=GEazFm1asY1FZO87siiV9kWu2Wnbu7xwx86nlPeRexk,14728
6
6
  pychemstation/control/README.md,sha256=ohPn3xhgjFyPraQqR4x9aZhurQVAOYuYHv-E8sj0eK4,3124
7
7
  pychemstation/control/__init__.py,sha256=7lSkY7Qa7Ikdz82-2FESc_oqktv7JndsjltCkiMqnMI,147
8
- pychemstation/control/hplc.py,sha256=Evmo_D6X2RDmPBFmo3vsKE9kxJzy36Ptz4MyF2IKNzA,13146
8
+ pychemstation/control/hplc.py,sha256=Nw5rJAOsmW1W6faq4jRuMJlEoGda91kD9GqkRX2jcgk,14210
9
9
  pychemstation/control/controllers/README.md,sha256=S5cd4NJmPjs6TUH98BtPJJhiS1Lu-mxLCNS786ogOrQ,32
10
10
  pychemstation/control/controllers/__init__.py,sha256=q2TUEie3J-OLlxcGLkG7vIy8fazCEhHm61OGzJPbhD0,179
11
- pychemstation/control/controllers/comm.py,sha256=jdKl9Islwk5tq2h1esm_-8gR4r4xZre0Ma5fMBqRzW4,6660
11
+ pychemstation/control/controllers/comm.py,sha256=ySjgMBIfJ11sygXiZSPp9Rf6ABM4t6JhZRONRj1u2Cc,6652
12
12
  pychemstation/control/controllers/data_aq/__init__.py,sha256=w-Zgbit10niOQfz780ZmRHjUFxV1hMkdui7fOMPqeLA,132
13
- pychemstation/control/controllers/data_aq/method.py,sha256=oxRT3kSjyrDzpS01HgtT9IvGkhq_wHQ_6_63maNhVio,18249
14
- pychemstation/control/controllers/data_aq/sequence.py,sha256=zhilGZXUEttqrux8OI2ieAWktINsrD98fm_b6guZKlo,17209
13
+ pychemstation/control/controllers/data_aq/method.py,sha256=8ONU_FN9sLFAv7r_rO8gN3hL3HJoMaYMOItlKCVAIsk,24773
14
+ pychemstation/control/controllers/data_aq/sequence.py,sha256=5JpXDWpS3JR2Cawn9RdUwOIYSipQPeB3OR7KW6muO6U,18623
15
15
  pychemstation/control/controllers/devices/__init__.py,sha256=QpgGnLXyWiB96KIB98wMccEi8oOUUaLxvBCyevJzcOg,75
16
16
  pychemstation/control/controllers/devices/injector.py,sha256=LyubM-fqf5ruseGx32deTDK-yevmaTOvdo6YKg2PF7I,4029
17
17
  pychemstation/generated/__init__.py,sha256=xnEs0QTjeuGYO3tVUIy8GDo95GqTV1peEjosGckpOu0,977
@@ -19,23 +19,23 @@ pychemstation/generated/dad_method.py,sha256=xTUiSCvkXcxBUhjVm1YZKu-tHs16k23pF-0
19
19
  pychemstation/generated/pump_method.py,sha256=s3MckKDw2-nZUC5lHrJVvXYdneWP8-9UvblNuGryPHY,12092
20
20
  pychemstation/utils/__init__.py,sha256=GZJyDzkhzrlMguxZTUpgthq72pA3YV23DJIR2Q63PCk,449
21
21
  pychemstation/utils/injector_types.py,sha256=z2iWwTklGm0GRDCL9pnPCovQrwyRwxv8w5w5Xh7Pj3U,1152
22
- pychemstation/utils/macro.py,sha256=n2CcvB7MY4fl8Ds909K41aqqH7_6DlKK5R4aemLhOdM,3300
23
- pychemstation/utils/method_types.py,sha256=pXfZWmIPnZu3kIdGPxSPJMhL4WSt-u00If1JDUaLaMQ,1555
22
+ pychemstation/utils/macro.py,sha256=VGOU380ruJSKQhXJFI1g--qg4Xgx-e0D8z5FDPGe0cA,3433
23
+ pychemstation/utils/method_types.py,sha256=ck8I4dRGhHXCUfBf3AT1OU1eCcSSZPgnlhvlLTfezEM,1562
24
24
  pychemstation/utils/num_utils.py,sha256=OpqZwMPoxTYkpjpinA1CcoQAXDY_0sie6d-hTU547uo,2087
25
25
  pychemstation/utils/parsing.py,sha256=mzdpxrH5ux4-_i4CwZvnIYnIwAnRnOptKb3fZyYJcx0,9307
26
- pychemstation/utils/sequence_types.py,sha256=XBK9FCWgTstmquSL69sQHk4oKb5dimmB-RWvwxU3RV4,2655
26
+ pychemstation/utils/sequence_types.py,sha256=H9htO2thyU9_KYOFBsGjlmA-4Nyd6aLd4D0MDLaXNCQ,2583
27
27
  pychemstation/utils/spec_utils.py,sha256=BtXgQndZy4kVKsrgEDxwNd0HctypnAt5upB9SOk1D9w,9700
28
- pychemstation/utils/table_types.py,sha256=obQrnwDNmy-7r1IDD3N6wY7gUPKfpcM6D9Ow0IN-T48,3609
28
+ pychemstation/utils/table_types.py,sha256=ZmV52Vl1cYG_C1PEmVGB02mC3Dhfi7QMkUZwe0ujzAg,3748
29
29
  pychemstation/utils/tray_types.py,sha256=UeHM0hUYaNc9giT96ZiGpyWBPQwG-SyLA0rVGzDDAJk,6618
30
30
  pychemstation/utils/abc_tables/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- pychemstation/utils/abc_tables/abc_comm.py,sha256=rTrnfyTdqRSiBfBW1_460KsHER5vpfhv7JYwObBgBZc,5501
31
+ pychemstation/utils/abc_tables/abc_comm.py,sha256=svCQZelFcqfNZY_0hHOL8frVtw_06_PasGoQTNFWQ7g,5625
32
32
  pychemstation/utils/abc_tables/device.py,sha256=v8MNFvymbKe4hWsqzO6Z_qsVvju_rISYtnPgaftecyE,919
33
- pychemstation/utils/abc_tables/run.py,sha256=FPwGuWIS4BLTS-aZqBLWoHvgev4qsAnUdPVjVPnFWVU,9842
34
- pychemstation/utils/abc_tables/table.py,sha256=4mfmegCdMppxgpxVDt7oULoxTBVia8_JTxYPjlWq8VE,7743
33
+ pychemstation/utils/abc_tables/run.py,sha256=uURDl66Mga8NAMffOUsG6prKhjpLo1-p2Y2PUTY1hY0,10052
34
+ pychemstation/utils/abc_tables/table.py,sha256=AbuitDcBHODV-KpxjL7ZPoE-TSa6gyG2uqXXY90Mv2o,7992
35
35
  pychemstation/utils/mocking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
- pychemstation/utils/mocking/mock_comm.py,sha256=4DcKmUxp-LYNXjywT_za1_GpqKa4sFTj7F2V3r_qsA0,156
37
- pychemstation/utils/mocking/mock_hplc.py,sha256=Hx6127C7d3miYGCZYxbN-Q3PU8kpMgXYX2n6we2Twgw,25
38
- pychemstation-0.10.7.dev1.dist-info/METADATA,sha256=4q5zU2Ys8DnagN8Jqi-AFj8Updt1JxCDGAr-g3L9peQ,5757
39
- pychemstation-0.10.7.dev1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
40
- pychemstation-0.10.7.dev1.dist-info/licenses/LICENSE,sha256=9bdF75gIf1MecZ7oymqWgJREVz7McXPG-mjqrTmzzD8,18658
41
- pychemstation-0.10.7.dev1.dist-info/RECORD,,
36
+ pychemstation/utils/mocking/mock_comm.py,sha256=vZeYBXaKBZOlJmhn4TSbkov62gqlkfztqf3MSFU9kLE,800
37
+ pychemstation/utils/mocking/mock_hplc.py,sha256=esVIlU4oqEsYLPOQs0AeVnKv9l52xBGT6UY862l9RQE,1163
38
+ pychemstation-0.10.8.dist-info/METADATA,sha256=MOopar8CgFbFX1BBqJAJmjB5Nao_0jO27TY9Y_pMEYc,5752
39
+ pychemstation-0.10.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
40
+ pychemstation-0.10.8.dist-info/licenses/LICENSE,sha256=9bdF75gIf1MecZ7oymqWgJREVz7McXPG-mjqrTmzzD8,18658
41
+ pychemstation-0.10.8.dist-info/RECORD,,