pychemstation 0.10.6__py3-none-any.whl → 0.10.7.dev1__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.
- pychemstation/analysis/base_spectrum.py +14 -15
 - pychemstation/analysis/chromatogram.py +7 -8
 - pychemstation/analysis/process_report.py +7 -15
 - pychemstation/control/README.md +2 -2
 - pychemstation/control/controllers/__init__.py +2 -1
 - pychemstation/control/controllers/comm.py +40 -13
 - pychemstation/control/controllers/data_aq/method.py +19 -22
 - pychemstation/control/controllers/data_aq/sequence.py +129 -111
 - pychemstation/control/controllers/devices/injector.py +7 -7
 - pychemstation/control/hplc.py +57 -60
 - pychemstation/utils/__init__.py +23 -0
 - pychemstation/utils/{mocking → abc_tables}/abc_comm.py +8 -14
 - pychemstation/utils/abc_tables/device.py +27 -0
 - pychemstation/{control/controllers → utils}/abc_tables/run.py +69 -34
 - pychemstation/{control/controllers → utils}/abc_tables/table.py +29 -22
 - pychemstation/utils/macro.py +13 -0
 - pychemstation/utils/method_types.py +12 -13
 - pychemstation/utils/mocking/mock_comm.py +1 -1
 - pychemstation/utils/num_utils.py +3 -3
 - pychemstation/utils/sequence_types.py +30 -12
 - pychemstation/utils/spec_utils.py +42 -66
 - pychemstation/utils/table_types.py +13 -2
 - pychemstation/utils/tray_types.py +28 -16
 - {pychemstation-0.10.6.dist-info → pychemstation-0.10.7.dev1.dist-info}/METADATA +2 -8
 - pychemstation-0.10.7.dev1.dist-info/RECORD +41 -0
 - pychemstation/control/controllers/abc_tables/device.py +0 -15
 - pychemstation/utils/pump_types.py +0 -7
 - pychemstation-0.10.6.dist-info/RECORD +0 -42
 - /pychemstation/{control/controllers → utils}/abc_tables/__init__.py +0 -0
 - {pychemstation-0.10.6.dist-info → pychemstation-0.10.7.dev1.dist-info}/WHEEL +0 -0
 - {pychemstation-0.10.6.dist-info → pychemstation-0.10.7.dev1.dist-info}/licenses/LICENSE +0 -0
 
    
        pychemstation/control/hplc.py
    CHANGED
    
    | 
         @@ -6,6 +6,7 @@ Authors: Lucy Hao 
     | 
|
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
            from __future__ import annotations
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
      
 9 
     | 
    
         
            +
            import os.path
         
     | 
| 
       9 
10 
     | 
    
         
             
            from typing import Dict, List, Optional, Tuple, Union
         
     | 
| 
       10 
11 
     | 
    
         | 
| 
       11 
12 
     | 
    
         
             
            from pychemstation.analysis.chromatogram import AgilentChannelChromatogramData
         
     | 
| 
         @@ -36,9 +37,9 @@ class HPLCController: 
     | 
|
| 
       36 
37 
     | 
    
         
             
                def __init__(
         
     | 
| 
       37 
38 
     | 
    
         
             
                    self,
         
     | 
| 
       38 
39 
     | 
    
         
             
                    comm_dir: str,
         
     | 
| 
       39 
     | 
    
         
            -
                    method_dir: str,
         
     | 
| 
       40 
     | 
    
         
            -
                    sequence_dir: str,
         
     | 
| 
       41 
     | 
    
         
            -
                     
     | 
| 
      
 40 
     | 
    
         
            +
                    method_dir: Optional[str] = None,
         
     | 
| 
      
 41 
     | 
    
         
            +
                    sequence_dir: Optional[str] = None,
         
     | 
| 
      
 42 
     | 
    
         
            +
                    extra_data_dirs: Optional[List[str]] = None,
         
     | 
| 
       42 
43 
     | 
    
         
             
                    offline: bool = False,
         
     | 
| 
       43 
44 
     | 
    
         
             
                    debug: bool = False,
         
     | 
| 
       44 
45 
     | 
    
         
             
                ):
         
     | 
| 
         @@ -47,37 +48,47 @@ class HPLCController: 
     | 
|
| 
       47 
48 
     | 
    
         
             
                    double escaped: "C:\\my_folder\\"
         
     | 
| 
       48 
49 
     | 
    
         | 
| 
       49 
50 
     | 
    
         
             
                    :param comm_dir: Name of directory for communication, where ChemStation will read and write from. Can be any existing directory.
         
     | 
| 
       50 
     | 
    
         
            -
                    :param data_dirs: Name of directories for storing data after method or sequence runs. Method data dir is default
         
     | 
| 
       51 
51 
     | 
    
         
             
                    the first one in the list. In other words, the first dir in the list is highest prio. Must be "normal" strings and not r-strings.
         
     | 
| 
       52 
     | 
    
         
            -
                    :param method_dir: Name of directory where method files are stored.
         
     | 
| 
       53 
     | 
    
         
            -
                    :param sequence_dir: Name of directory where sequence files are stored.
         
     | 
| 
       54 
52 
     | 
    
         
             
                    :raises FileNotFoundError: If either `data_dir`, `method_dir`, `sequence_dir`, `sequence_data_dir`or `comm_dir` is not a valid directory.
         
     | 
| 
       55 
53 
     | 
    
         
             
                    """
         
     | 
| 
       56 
54 
     | 
    
         
             
                    self.comm: CommunicationController = CommunicationController(
         
     | 
| 
       57 
     | 
    
         
            -
                        comm_dir=comm_dir, debug=debug
         
     | 
| 
       58 
     | 
    
         
            -
                    )
         
     | 
| 
       59 
     | 
    
         
            -
                    self.method_controller: MethodController = MethodController(
         
     | 
| 
       60 
     | 
    
         
            -
                        controller=self.comm,
         
     | 
| 
       61 
     | 
    
         
            -
                        src=method_dir,
         
     | 
| 
       62 
     | 
    
         
            -
                        data_dirs=data_dirs,
         
     | 
| 
       63 
     | 
    
         
            -
                        table=self.METHOD_TIMETABLE,
         
     | 
| 
       64 
     | 
    
         
            -
                        offline=offline,
         
     | 
| 
       65 
     | 
    
         
            -
                        injector_controller=InjectorController(
         
     | 
| 
       66 
     | 
    
         
            -
                            controller=self.comm, table=self.INJECTOR_TABLE, offline=offline
         
     | 
| 
       67 
     | 
    
         
            -
                        ),
         
     | 
| 
       68 
     | 
    
         
            -
                    )
         
     | 
| 
       69 
     | 
    
         
            -
                    self.sequence_controller: SequenceController = SequenceController(
         
     | 
| 
       70 
     | 
    
         
            -
                        controller=self.comm,
         
     | 
| 
       71 
     | 
    
         
            -
                        src=sequence_dir,
         
     | 
| 
       72 
     | 
    
         
            -
                        data_dirs=data_dirs,
         
     | 
| 
       73 
     | 
    
         
            -
                        table=self.SEQUENCE_TABLE,
         
     | 
| 
       74 
     | 
    
         
            -
                        method_controller=self.method_controller,
         
     | 
| 
       75 
     | 
    
         
            -
                        offline=offline,
         
     | 
| 
      
 55 
     | 
    
         
            +
                        comm_dir=comm_dir, debug=debug, offline=offline
         
     | 
| 
       76 
56 
     | 
    
         
             
                    )
         
     | 
| 
      
 57 
     | 
    
         
            +
                    data_dirs: List[str] = []
         
     | 
| 
      
 58 
     | 
    
         
            +
                    if not offline:
         
     | 
| 
      
 59 
     | 
    
         
            +
                        if not method_dir or not sequence_dir or not extra_data_dirs:
         
     | 
| 
      
 60 
     | 
    
         
            +
                            method_dir, sequence_dir, data_dirs = self.comm.get_chemstation_dirs()
         
     | 
| 
      
 61 
     | 
    
         
            +
                        if extra_data_dirs:
         
     | 
| 
      
 62 
     | 
    
         
            +
                            data_dirs.extend(extra_data_dirs)
         
     | 
| 
      
 63 
     | 
    
         
            +
                        data_dirs = list(set([os.path.normpath(p) for p in data_dirs]))
         
     | 
| 
      
 64 
     | 
    
         
            +
                    if (method_dir and sequence_dir and data_dirs and not offline) or offline:
         
     | 
| 
      
 65 
     | 
    
         
            +
                        self.method_controller: MethodController = MethodController(
         
     | 
| 
      
 66 
     | 
    
         
            +
                            controller=self.comm,
         
     | 
| 
      
 67 
     | 
    
         
            +
                            src=method_dir,
         
     | 
| 
      
 68 
     | 
    
         
            +
                            data_dirs=data_dirs,
         
     | 
| 
      
 69 
     | 
    
         
            +
                            table=self.METHOD_TIMETABLE,
         
     | 
| 
      
 70 
     | 
    
         
            +
                            offline=offline,
         
     | 
| 
      
 71 
     | 
    
         
            +
                            injector_controller=InjectorController(
         
     | 
| 
      
 72 
     | 
    
         
            +
                                controller=self.comm, table=self.INJECTOR_TABLE, offline=offline
         
     | 
| 
      
 73 
     | 
    
         
            +
                            ),
         
     | 
| 
      
 74 
     | 
    
         
            +
                        )
         
     | 
| 
      
 75 
     | 
    
         
            +
                        self.sequence_controller: SequenceController = SequenceController(
         
     | 
| 
      
 76 
     | 
    
         
            +
                            controller=self.comm,
         
     | 
| 
      
 77 
     | 
    
         
            +
                            src=sequence_dir,
         
     | 
| 
      
 78 
     | 
    
         
            +
                            data_dirs=data_dirs,
         
     | 
| 
      
 79 
     | 
    
         
            +
                            table=self.SEQUENCE_TABLE,
         
     | 
| 
      
 80 
     | 
    
         
            +
                            method_controller=self.method_controller,
         
     | 
| 
      
 81 
     | 
    
         
            +
                            offline=offline,
         
     | 
| 
      
 82 
     | 
    
         
            +
                        )
         
     | 
| 
      
 83 
     | 
    
         
            +
                    elif not offline and (not method_dir or not sequence_dir or not data_dirs):
         
     | 
| 
      
 84 
     | 
    
         
            +
                        raise ValueError(
         
     | 
| 
      
 85 
     | 
    
         
            +
                            f"Expected a method dir: {method_dir}, sequence dir: {sequence_dir} and data dirs:{data_dirs} but one was None."
         
     | 
| 
      
 86 
     | 
    
         
            +
                        )
         
     | 
| 
      
 87 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 88 
     | 
    
         
            +
                        raise ValueError("Expected error occured, please try again.")
         
     | 
| 
       77 
89 
     | 
    
         | 
| 
       78 
90 
     | 
    
         
             
                def send(self, cmd: Union[Command, str]):
         
     | 
| 
       79 
     | 
    
         
            -
                    """
         
     | 
| 
       80 
     | 
    
         
            -
                    Sends any Command or string to Chemstation.
         
     | 
| 
      
 91 
     | 
    
         
            +
                    """Sends any Command or string to Chemstation.
         
     | 
| 
       81 
92 
     | 
    
         | 
| 
       82 
93 
     | 
    
         
             
                    :param cmd: the macro to send to Chemstation
         
     | 
| 
       83 
94 
     | 
    
         
             
                    """
         
     | 
| 
         @@ -88,10 +99,9 @@ class HPLCController: 
     | 
|
| 
       88 
99 
     | 
    
         
             
                    self.comm.send(cmd)
         
     | 
| 
       89 
100 
     | 
    
         | 
| 
       90 
101 
     | 
    
         
             
                def receive(self) -> None | Response | str:
         
     | 
| 
       91 
     | 
    
         
            -
                    """
         
     | 
| 
       92 
     | 
    
         
            -
                    Get the most recent response from Chemstation.
         
     | 
| 
      
 102 
     | 
    
         
            +
                    """Get the most recent response from Chemstation.
         
     | 
| 
       93 
103 
     | 
    
         | 
| 
       94 
     | 
    
         
            -
                    :return: most recent response from  
     | 
| 
      
 104 
     | 
    
         
            +
                    :return: most recent response from the most recently sent MACRO that returned a response.
         
     | 
| 
       95 
105 
     | 
    
         
             
                    """
         
     | 
| 
       96 
106 
     | 
    
         
             
                    if not self.comm:
         
     | 
| 
       97 
107 
     | 
    
         
             
                        raise RuntimeError(
         
     | 
| 
         @@ -100,8 +110,7 @@ class HPLCController: 
     | 
|
| 
       100 
110 
     | 
    
         
             
                    return self.comm.receive().value
         
     | 
| 
       101 
111 
     | 
    
         | 
| 
       102 
112 
     | 
    
         
             
                def status(self) -> Status:
         
     | 
| 
       103 
     | 
    
         
            -
                    """
         
     | 
| 
       104 
     | 
    
         
            -
                    Get the current status of the HPLC machine.
         
     | 
| 
      
 113 
     | 
    
         
            +
                    """Get the current status of the HPLC machine.
         
     | 
| 
       105 
114 
     | 
    
         | 
| 
       106 
115 
     | 
    
         
             
                    :return: current status of the HPLC machine; Status types can be found in `pychemstation.utils.macro`
         
     | 
| 
       107 
116 
     | 
    
         
             
                    """
         
     | 
| 
         @@ -112,8 +121,7 @@ class HPLCController: 
     | 
|
| 
       112 
121 
     | 
    
         
             
                    return self.comm.get_status()
         
     | 
| 
       113 
122 
     | 
    
         | 
| 
       114 
123 
     | 
    
         
             
                def switch_method(self, method_name: str):
         
     | 
| 
       115 
     | 
    
         
            -
                    """
         
     | 
| 
       116 
     | 
    
         
            -
                    Allows the user to switch between pre-programmed methods. No need to append '.M'
         
     | 
| 
      
 124 
     | 
    
         
            +
                    """Allows the user to switch between pre-programmed methods. No need to append '.M'
         
     | 
| 
       117 
125 
     | 
    
         
             
                    to the end of the method name. For example. for the method named 'General-Poroshell.M',
         
     | 
| 
       118 
126 
     | 
    
         
             
                    only 'General-Poroshell' is needed.
         
     | 
| 
       119 
127 
     | 
    
         | 
| 
         @@ -124,8 +132,7 @@ class HPLCController: 
     | 
|
| 
       124 
132 
     | 
    
         
             
                    self.method_controller.switch(method_name)
         
     | 
| 
       125 
133 
     | 
    
         | 
| 
       126 
134 
     | 
    
         
             
                def switch_sequence(self, sequence_name: str):
         
     | 
| 
       127 
     | 
    
         
            -
                    """
         
     | 
| 
       128 
     | 
    
         
            -
                     Allows the user to switch between pre-programmed sequences. The sequence name does not need the '.S' extension.
         
     | 
| 
      
 135 
     | 
    
         
            +
                    """Allows the user to switch between pre-programmed sequences. The sequence name does not need the '.S' extension.
         
     | 
| 
       129 
136 
     | 
    
         
             
                     For example: for the method named 'mySeq.S', only 'mySeq' is needed.
         
     | 
| 
       130 
137 
     | 
    
         | 
| 
       131 
138 
     | 
    
         
             
                    :param sequence_name: The name of the sequence file
         
     | 
| 
         @@ -138,8 +145,7 @@ class HPLCController: 
     | 
|
| 
       138 
145 
     | 
    
         
             
                    add_timestamp: bool = True,
         
     | 
| 
       139 
146 
     | 
    
         
             
                    stall_while_running: bool = True,
         
     | 
| 
       140 
147 
     | 
    
         
             
                ):
         
     | 
| 
       141 
     | 
    
         
            -
                    """
         
     | 
| 
       142 
     | 
    
         
            -
                    This is the preferred method to trigger a run.
         
     | 
| 
      
 148 
     | 
    
         
            +
                    """This is the preferred method to trigger a run.
         
     | 
| 
       143 
149 
     | 
    
         
             
                    Starts the currently selected method, storing data
         
     | 
| 
       144 
150 
     | 
    
         
             
                    under the <data_dir>/<experiment_name>.D folder.
         
     | 
| 
       145 
151 
     | 
    
         
             
                    Device must be ready.
         
     | 
| 
         @@ -159,8 +165,7 @@ class HPLCController: 
     | 
|
| 
       159 
165 
     | 
    
         
             
                    self.method_controller.stop()
         
     | 
| 
       160 
166 
     | 
    
         | 
| 
       161 
167 
     | 
    
         
             
                def run_sequence(self, stall_while_running: bool = True):
         
     | 
| 
       162 
     | 
    
         
            -
                    """
         
     | 
| 
       163 
     | 
    
         
            -
                    Starts the currently loaded sequence, storing data
         
     | 
| 
      
 168 
     | 
    
         
            +
                    """Starts the currently loaded sequence, storing data
         
     | 
| 
       164 
169 
     | 
    
         
             
                    under one of the data_dirs/<sequence table name> folder.
         
     | 
| 
       165 
170 
     | 
    
         
             
                    Device must be ready.
         
     | 
| 
       166 
171 
     | 
    
         | 
| 
         @@ -169,24 +174,21 @@ class HPLCController: 
     | 
|
| 
       169 
174 
     | 
    
         
             
                    self.sequence_controller.run(stall_while_running=stall_while_running)
         
     | 
| 
       170 
175 
     | 
    
         | 
| 
       171 
176 
     | 
    
         
             
                def check_method_complete(self) -> Tuple[float, int]:
         
     | 
| 
       172 
     | 
    
         
            -
                    """
         
     | 
| 
       173 
     | 
    
         
            -
                    Check if the currently running method (if any) is done.
         
     | 
| 
      
 177 
     | 
    
         
            +
                    """Check if the currently running method (if any) is done.
         
     | 
| 
       174 
178 
     | 
    
         | 
| 
       175 
     | 
    
         
            -
                    :returns the percent of the method run completed, and whether the run is complete.
         
     | 
| 
      
 179 
     | 
    
         
            +
                    :returns: the percent of the method run completed, and whether the run is complete.
         
     | 
| 
       176 
180 
     | 
    
         
             
                    """
         
     | 
| 
       177 
181 
     | 
    
         
             
                    return self.method_controller.check_hplc_run_finished()
         
     | 
| 
       178 
182 
     | 
    
         | 
| 
       179 
183 
     | 
    
         
             
                def check_sequence_complete(self) -> Tuple[float, int]:
         
     | 
| 
       180 
     | 
    
         
            -
                    """
         
     | 
| 
       181 
     | 
    
         
            -
                    Check if the currently running sequence (if any) is done.
         
     | 
| 
      
 184 
     | 
    
         
            +
                    """Check if the currently running sequence (if any) is done.
         
     | 
| 
       182 
185 
     | 
    
         | 
| 
       183 
186 
     | 
    
         
             
                    :return: the percent of the sequence run completed, and whether the run is complete.
         
     | 
| 
       184 
187 
     | 
    
         
             
                    """
         
     | 
| 
       185 
188 
     | 
    
         
             
                    return self.sequence_controller.check_hplc_run_finished()
         
     | 
| 
       186 
189 
     | 
    
         | 
| 
       187 
190 
     | 
    
         
             
                def edit_method(self, updated_method: MethodDetails, save: bool = False):
         
     | 
| 
       188 
     | 
    
         
            -
                    """
         
     | 
| 
       189 
     | 
    
         
            -
                    Updated the currently loaded method in ChemStation with provided values.
         
     | 
| 
      
 191 
     | 
    
         
            +
                    """Updated the currently loaded method in ChemStation with provided values.
         
     | 
| 
       190 
192 
     | 
    
         | 
| 
       191 
193 
     | 
    
         
             
                    :param updated_method: the method with updated values, to be sent to Chemstation to modify the currently loaded method.
         
     | 
| 
       192 
194 
     | 
    
         
             
                    :param save: whether this method should be saved to disk, or just modified.
         
     | 
| 
         @@ -194,8 +196,7 @@ class HPLCController: 
     | 
|
| 
       194 
196 
     | 
    
         
             
                    self.method_controller.edit(updated_method, save)
         
     | 
| 
       195 
197 
     | 
    
         | 
| 
       196 
198 
     | 
    
         
             
                def edit_sequence(self, updated_sequence: SequenceTable):
         
     | 
| 
       197 
     | 
    
         
            -
                    """
         
     | 
| 
       198 
     | 
    
         
            -
                    Updates the currently loaded sequence table with the provided table, and saves the sequence.
         
     | 
| 
      
 199 
     | 
    
         
            +
                    """Updates the currently loaded sequence table with the provided table, and saves the sequence.
         
     | 
| 
       199 
200 
     | 
    
         | 
| 
       200 
201 
     | 
    
         
             
                    :param updated_sequence: The sequence table to be written to the currently loaded sequence table.
         
     | 
| 
       201 
202 
     | 
    
         
             
                    """
         
     | 
| 
         @@ -206,8 +207,7 @@ class HPLCController: 
     | 
|
| 
       206 
207 
     | 
    
         
             
                    custom_path: Optional[str] = None,
         
     | 
| 
       207 
208 
     | 
    
         
             
                    report_type: ReportType = ReportType.CSV,
         
     | 
| 
       208 
209 
     | 
    
         
             
                ) -> AgilentReport:
         
     | 
| 
       209 
     | 
    
         
            -
                    """
         
     | 
| 
       210 
     | 
    
         
            -
                    Return data contained in the REPORT files. Use `aghplctools` if you want more report processing utility.
         
     | 
| 
      
 210 
     | 
    
         
            +
                    """Return data contained in the REPORT files. Use `aghplctools` if you want more report processing utility.
         
     | 
| 
       211 
211 
     | 
    
         | 
| 
       212 
212 
     | 
    
         
             
                    :param custom_path: path to sequence folder
         
     | 
| 
       213 
213 
     | 
    
         
             
                    :param report_type: read either the TXT or CSV version
         
     | 
| 
         @@ -220,8 +220,7 @@ class HPLCController: 
     | 
|
| 
       220 
220 
     | 
    
         
             
                def get_last_run_method_data(
         
     | 
| 
       221 
221 
     | 
    
         
             
                    self, read_uv: bool = False, custom_path: Optional[str] = None
         
     | 
| 
       222 
222 
     | 
    
         
             
                ) -> Dict[int, AgilentHPLCChromatogram] | AgilentChannelChromatogramData:
         
     | 
| 
       223 
     | 
    
         
            -
                    """
         
     | 
| 
       224 
     | 
    
         
            -
                    Returns the last run method data.
         
     | 
| 
      
 223 
     | 
    
         
            +
                    """Returns the last run method data.
         
     | 
| 
       225 
224 
     | 
    
         | 
| 
       226 
225 
     | 
    
         
             
                    :param custom_path: If you want to just load method data but from a file path. This file path must be the complete file path.
         
     | 
| 
       227 
226 
     | 
    
         
             
                    :param read_uv: whether to also read the UV file
         
     | 
| 
         @@ -236,8 +235,7 @@ class HPLCController: 
     | 
|
| 
       236 
235 
     | 
    
         
             
                    custom_path: Optional[str] = None,
         
     | 
| 
       237 
236 
     | 
    
         
             
                    report_type: ReportType = ReportType.CSV,
         
     | 
| 
       238 
237 
     | 
    
         
             
                ) -> List[AgilentReport]:
         
     | 
| 
       239 
     | 
    
         
            -
                    """
         
     | 
| 
       240 
     | 
    
         
            -
                    Return data contained in the REPORT files. Use `aghplctools` if you want more report processing utility.
         
     | 
| 
      
 238 
     | 
    
         
            +
                    """Return data contained in the REPORT files. Use `aghplctools` if you want more report processing utility.
         
     | 
| 
       241 
239 
     | 
    
         | 
| 
       242 
240 
     | 
    
         
             
                    :param custom_path: path to sequence folder
         
     | 
| 
       243 
241 
     | 
    
         
             
                    :param report_type: read either the TXT or CSV version
         
     | 
| 
         @@ -252,8 +250,7 @@ class HPLCController: 
     | 
|
| 
       252 
250 
     | 
    
         
             
                ) -> (
         
     | 
| 
       253 
251 
     | 
    
         
             
                    List[Dict[int, AgilentHPLCChromatogram]] | List[AgilentChannelChromatogramData]
         
     | 
| 
       254 
252 
     | 
    
         
             
                ):
         
     | 
| 
       255 
     | 
    
         
            -
                    """
         
     | 
| 
       256 
     | 
    
         
            -
                    Returns data for all rows in the last run sequence data.
         
     | 
| 
      
 253 
     | 
    
         
            +
                    """Returns data for all rows in the last run sequence data.
         
     | 
| 
       257 
254 
     | 
    
         | 
| 
       258 
255 
     | 
    
         
             
                    :param custom_path: If you want to just load sequence data but from a file path. This file path must be the complete file path.
         
     | 
| 
       259 
256 
     | 
    
         
             
                    :param read_uv: whether to also read the UV file
         
     | 
| 
         @@ -265,11 +262,11 @@ class HPLCController: 
     | 
|
| 
       265 
262 
     | 
    
         | 
| 
       266 
263 
     | 
    
         
             
                def check_loaded_sequence(self) -> str:
         
     | 
| 
       267 
264 
     | 
    
         
             
                    """Returns the name of the currently loaded sequence."""
         
     | 
| 
       268 
     | 
    
         
            -
                    return self.sequence_controller. 
     | 
| 
      
 265 
     | 
    
         
            +
                    return self.sequence_controller.get_current_sequence_name()
         
     | 
| 
       269 
266 
     | 
    
         | 
| 
       270 
267 
     | 
    
         
             
                def check_loaded_method(self) -> str:
         
     | 
| 
       271 
268 
     | 
    
         
             
                    """Returns the name of the currently loaded method."""
         
     | 
| 
       272 
     | 
    
         
            -
                    return self.method_controller. 
     | 
| 
      
 269 
     | 
    
         
            +
                    return self.method_controller.get_current_method_name()
         
     | 
| 
       273 
270 
     | 
    
         | 
| 
       274 
271 
     | 
    
         
             
                def load_method(self) -> MethodDetails:
         
     | 
| 
       275 
272 
     | 
    
         
             
                    """Returns details of the currently loaded method, such as its starting modifier conditions and timetable."""
         
     | 
| 
         @@ -279,7 +276,7 @@ class HPLCController: 
     | 
|
| 
       279 
276 
     | 
    
         
             
                    """Returns the currently loaded sequence."""
         
     | 
| 
       280 
277 
     | 
    
         
             
                    return self.sequence_controller.load()
         
     | 
| 
       281 
278 
     | 
    
         | 
| 
       282 
     | 
    
         
            -
                def load_injector_program(self) -> InjectorTable:
         
     | 
| 
      
 279 
     | 
    
         
            +
                def load_injector_program(self) -> InjectorTable | None:
         
     | 
| 
       283 
280 
     | 
    
         
             
                    return self.method_controller.injector_controller.load()
         
     | 
| 
       284 
281 
     | 
    
         | 
| 
       285 
282 
     | 
    
         
             
                def standby(self):
         
     | 
    
        pychemstation/utils/__init__.py
    CHANGED
    
    | 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from . import abc_tables
         
     | 
| 
      
 2 
     | 
    
         
            +
            from . import injector_types
         
     | 
| 
      
 3 
     | 
    
         
            +
            from . import macro
         
     | 
| 
      
 4 
     | 
    
         
            +
            from . import method_types
         
     | 
| 
      
 5 
     | 
    
         
            +
            from . import num_utils
         
     | 
| 
      
 6 
     | 
    
         
            +
            from . import parsing
         
     | 
| 
      
 7 
     | 
    
         
            +
            from . import sequence_types
         
     | 
| 
      
 8 
     | 
    
         
            +
            from . import spec_utils
         
     | 
| 
      
 9 
     | 
    
         
            +
            from . import table_types
         
     | 
| 
      
 10 
     | 
    
         
            +
            from . import tray_types
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            __all__ = [
         
     | 
| 
      
 13 
     | 
    
         
            +
                "abc_tables",
         
     | 
| 
      
 14 
     | 
    
         
            +
                "injector_types",
         
     | 
| 
      
 15 
     | 
    
         
            +
                "macro",
         
     | 
| 
      
 16 
     | 
    
         
            +
                "method_types",
         
     | 
| 
      
 17 
     | 
    
         
            +
                "num_utils",
         
     | 
| 
      
 18 
     | 
    
         
            +
                "parsing",
         
     | 
| 
      
 19 
     | 
    
         
            +
                "sequence_types",
         
     | 
| 
      
 20 
     | 
    
         
            +
                "spec_utils",
         
     | 
| 
      
 21 
     | 
    
         
            +
                "table_types",
         
     | 
| 
      
 22 
     | 
    
         
            +
                "tray_types",
         
     | 
| 
      
 23 
     | 
    
         
            +
            ]
         
     | 
| 
         @@ -18,17 +18,17 @@ from typing import Union 
     | 
|
| 
       18 
18 
     | 
    
         | 
| 
       19 
19 
     | 
    
         
             
            from result import Err, Ok, Result
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
            from  
     | 
| 
       22 
     | 
    
         
            -
                HPLCAvailStatus,
         
     | 
| 
       23 
     | 
    
         
            -
                Command,
         
     | 
| 
       24 
     | 
    
         
            -
                Status,
         
     | 
| 
       25 
     | 
    
         
            -
                Response,
         
     | 
| 
       26 
     | 
    
         
            -
            )
         
     | 
| 
      
 21 
     | 
    
         
            +
            from ..macro import HPLCAvailStatus, Command, Response, Status
         
     | 
| 
       27 
22 
     | 
    
         | 
| 
       28 
23 
     | 
    
         | 
| 
       29 
24 
     | 
    
         
             
            class ABCCommunicationController(abc.ABC):
         
     | 
| 
       30 
     | 
    
         
            -
                """
         
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
      
 25 
     | 
    
         
            +
                """Abstract class representing the communication controller.
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                :param comm_dir: the complete directory path that was used in the MACRO file, common file that pychemstation and Chemstation use to communicate.
         
     | 
| 
      
 28 
     | 
    
         
            +
                :param cmd_file: name of the write file that pychemstation writes MACROs to, in `comm_dir`
         
     | 
| 
      
 29 
     | 
    
         
            +
                :param reply_file: name of the read file that Chemstation replies to, in `comm_dir
         
     | 
| 
      
 30 
     | 
    
         
            +
                :param offline: whether or not communication with Chemstation is to be established
         
     | 
| 
      
 31 
     | 
    
         
            +
                :param debug: if True, prints all send MACROs to an out.txt file
         
     | 
| 
       32 
32 
     | 
    
         
             
                """
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
34 
     | 
    
         
             
                # maximum command number
         
     | 
| 
         @@ -42,12 +42,6 @@ class ABCCommunicationController(abc.ABC): 
     | 
|
| 
       42 
42 
     | 
    
         
             
                    offline: bool = False,
         
     | 
| 
       43 
43 
     | 
    
         
             
                    debug: bool = False,
         
     | 
| 
       44 
44 
     | 
    
         
             
                ):
         
     | 
| 
       45 
     | 
    
         
            -
                    """
         
     | 
| 
       46 
     | 
    
         
            -
                    :param comm_dir:
         
     | 
| 
       47 
     | 
    
         
            -
                    :param cmd_file: Name of command file
         
     | 
| 
       48 
     | 
    
         
            -
                    :param reply_file: Name of reply file
         
     | 
| 
       49 
     | 
    
         
            -
                    :param debug: whether to save log of sent commands
         
     | 
| 
       50 
     | 
    
         
            -
                    """
         
     | 
| 
       51 
45 
     | 
    
         
             
                    if not offline:
         
     | 
| 
       52 
46 
     | 
    
         
             
                        self.debug = debug
         
     | 
| 
       53 
47 
     | 
    
         
             
                        if os.path.isdir(comm_dir):
         
     | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from __future__ import annotations
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            from abc import ABC
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            from .table import ABCTableController
         
     | 
| 
      
 6 
     | 
    
         
            +
            from ..table_types import Table
         
     | 
| 
      
 7 
     | 
    
         
            +
            from ...control.controllers import CommunicationController
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            class DeviceController(ABCTableController, ABC):
         
     | 
| 
      
 11 
     | 
    
         
            +
                """Abstract controller representing tables that contain device information.
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                :param controller: controller for sending MACROs
         
     | 
| 
      
 14 
     | 
    
         
            +
                :param table: contains register keys for accessing table in Chemstation
         
     | 
| 
      
 15 
     | 
    
         
            +
                :param offline: whether the communication controller is online.
         
     | 
| 
      
 16 
     | 
    
         
            +
                """
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def __init__(
         
     | 
| 
      
 19 
     | 
    
         
            +
                    self, controller: CommunicationController, table: Table, offline: bool
         
     | 
| 
      
 20 
     | 
    
         
            +
                ):
         
     | 
| 
      
 21 
     | 
    
         
            +
                    super().__init__(controller=controller, table=table)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    self.offline = offline
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def __new__(cls, *args, **kwargs):
         
     | 
| 
      
 25 
     | 
    
         
            +
                    if cls is ABCTableController:
         
     | 
| 
      
 26 
     | 
    
         
            +
                        raise TypeError(f"only children of '{cls.__name__}' may be instantiated")
         
     | 
| 
      
 27 
     | 
    
         
            +
                    return object.__new__(cls)
         
     | 
| 
         @@ -11,60 +11,69 @@ import math 
     | 
|
| 
       11 
11 
     | 
    
         
             
            import os
         
     | 
| 
       12 
12 
     | 
    
         
             
            import time
         
     | 
| 
       13 
13 
     | 
    
         
             
            import warnings
         
     | 
| 
       14 
     | 
    
         
            -
            from typing import Dict, List, Optional, Tuple, Union
         
     | 
| 
      
 14 
     | 
    
         
            +
            from typing import Dict, List, Optional, Tuple, Union, Set
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
16 
     | 
    
         
             
            import polling
         
     | 
| 
       17 
17 
     | 
    
         
             
            import rainbow as rb
         
     | 
| 
       18 
     | 
    
         
            -
            from result import Err,  
     | 
| 
      
 18 
     | 
    
         
            +
            from result import Err, Ok, Result
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            from ..macro import HPLCRunningStatus, Command
         
     | 
| 
      
 21 
     | 
    
         
            +
            from ..method_types import MethodDetails
         
     | 
| 
      
 22 
     | 
    
         
            +
            from ..sequence_types import SequenceTable
         
     | 
| 
      
 23 
     | 
    
         
            +
            from ..table_types import Table, T
         
     | 
| 
      
 24 
     | 
    
         
            +
            from ...analysis.chromatogram import (
         
     | 
| 
      
 25 
     | 
    
         
            +
                AgilentChannelChromatogramData,
         
     | 
| 
      
 26 
     | 
    
         
            +
                AgilentHPLCChromatogram,
         
     | 
| 
      
 27 
     | 
    
         
            +
            )
         
     | 
| 
       19 
28 
     | 
    
         | 
| 
       20 
29 
     | 
    
         
             
            from .table import ABCTableController
         
     | 
| 
       21 
     | 
    
         
            -
            from  
     | 
| 
      
 30 
     | 
    
         
            +
            from ...analysis.process_report import (
         
     | 
| 
      
 31 
     | 
    
         
            +
                ReportType,
         
     | 
| 
       22 
32 
     | 
    
         
             
                AgilentReport,
         
     | 
| 
       23 
33 
     | 
    
         
             
                CSVProcessor,
         
     | 
| 
       24 
     | 
    
         
            -
                ReportType,
         
     | 
| 
       25 
34 
     | 
    
         
             
                TXTProcessor,
         
     | 
| 
       26 
35 
     | 
    
         
             
            )
         
     | 
| 
       27 
     | 
    
         
            -
            from  
     | 
| 
       28 
     | 
    
         
            -
            from pychemstation.analysis.chromatogram import (
         
     | 
| 
       29 
     | 
    
         
            -
                AgilentChannelChromatogramData,
         
     | 
| 
       30 
     | 
    
         
            -
                AgilentHPLCChromatogram,
         
     | 
| 
       31 
     | 
    
         
            -
            )
         
     | 
| 
       32 
     | 
    
         
            -
            from ....utils.macro import HPLCRunningStatus
         
     | 
| 
       33 
     | 
    
         
            -
            from ....utils.method_types import MethodDetails
         
     | 
| 
       34 
     | 
    
         
            -
            from ....utils.sequence_types import SequenceTable
         
     | 
| 
       35 
     | 
    
         
            -
            from ....utils.table_types import Table, T
         
     | 
| 
      
 36 
     | 
    
         
            +
            from ...control.controllers import CommunicationController
         
     | 
| 
       36 
37 
     | 
    
         | 
| 
       37 
38 
     | 
    
         
             
            TableType = Union[MethodDetails, SequenceTable]
         
     | 
| 
       38 
39 
     | 
    
         | 
| 
       39 
40 
     | 
    
         | 
| 
       40 
41 
     | 
    
         
             
            class RunController(ABCTableController, abc.ABC):
         
     | 
| 
      
 42 
     | 
    
         
            +
                """Abstract controller for all tables that can trigger runs on Chemstation.
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                :param controller: the controller for sending MACROs, must be initialized for a run to be triggered.
         
     | 
| 
      
 45 
     | 
    
         
            +
                :param src: complete directory path where files containing run parameters are stored.
         
     | 
| 
      
 46 
     | 
    
         
            +
                :param data_dirs: list of complete directories that Chemstation will write data to.
         
     | 
| 
      
 47 
     | 
    
         
            +
                :param table: contains register keys for accessing table in Chemstation.
         
     | 
| 
      
 48 
     | 
    
         
            +
                :param offline: whether the communication controller is online.
         
     | 
| 
      
 49 
     | 
    
         
            +
                """
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
       41 
51 
     | 
    
         
             
                def __init__(
         
     | 
| 
       42 
52 
     | 
    
         
             
                    self,
         
     | 
| 
       43 
53 
     | 
    
         
             
                    controller: Optional[CommunicationController],
         
     | 
| 
       44 
     | 
    
         
            -
                    src: str,
         
     | 
| 
       45 
     | 
    
         
            -
                    data_dirs: List[str],
         
     | 
| 
      
 54 
     | 
    
         
            +
                    src: Optional[str],
         
     | 
| 
      
 55 
     | 
    
         
            +
                    data_dirs: Optional[List[str]],
         
     | 
| 
       46 
56 
     | 
    
         
             
                    table: Table,
         
     | 
| 
       47 
57 
     | 
    
         
             
                    offline: bool = False,
         
     | 
| 
       48 
58 
     | 
    
         
             
                ):
         
     | 
| 
       49 
59 
     | 
    
         
             
                    super().__init__(controller=controller, table=table)
         
     | 
| 
       50 
     | 
    
         
            -
                    warnings.warn(
         
     | 
| 
       51 
     | 
    
         
            -
                        "This abstract class is not meant to be initialized. Use MethodController or SequenceController."
         
     | 
| 
       52 
     | 
    
         
            -
                    )
         
     | 
| 
       53 
60 
     | 
    
         
             
                    self.table_state: Optional[TableType] = None
         
     | 
| 
       54 
61 
     | 
    
         
             
                    self.curr_run_starting_time: Optional[float] = None
         
     | 
| 
       55 
62 
     | 
    
         
             
                    self.timeout: Optional[float] = None
         
     | 
| 
      
 63 
     | 
    
         
            +
                    self.current_run_child_files: Set[str] = set()
         
     | 
| 
       56 
64 
     | 
    
         | 
| 
       57 
65 
     | 
    
         
             
                    if not offline:
         
     | 
| 
       58 
66 
     | 
    
         
             
                        if src and not os.path.isdir(src):
         
     | 
| 
       59 
67 
     | 
    
         
             
                            raise FileNotFoundError(f"dir: {src} not found.")
         
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
             
     | 
| 
       66 
     | 
    
         
            -
                         
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
      
 68 
     | 
    
         
            +
                        if data_dirs:
         
     | 
| 
      
 69 
     | 
    
         
            +
                            for d in data_dirs:
         
     | 
| 
      
 70 
     | 
    
         
            +
                                if not os.path.isdir(d):
         
     | 
| 
      
 71 
     | 
    
         
            +
                                    raise FileNotFoundError(f"dir: {d} not found.")
         
     | 
| 
      
 72 
     | 
    
         
            +
                                if r"\\" in d:
         
     | 
| 
      
 73 
     | 
    
         
            +
                                    raise ValueError("Data directories should not be raw strings!")
         
     | 
| 
      
 74 
     | 
    
         
            +
                        if src and data_dirs:
         
     | 
| 
      
 75 
     | 
    
         
            +
                            self.src: str = src
         
     | 
| 
      
 76 
     | 
    
         
            +
                            self.data_dirs: List[str] = data_dirs
         
     | 
| 
       68 
77 
     | 
    
         | 
| 
       69 
78 
     | 
    
         
             
                    self.spectra: dict[str, AgilentHPLCChromatogram] = {
         
     | 
| 
       70 
79 
     | 
    
         
             
                        "A": AgilentHPLCChromatogram(),
         
     | 
| 
         @@ -79,8 +88,15 @@ class RunController(ABCTableController, abc.ABC): 
     | 
|
| 
       79 
88 
     | 
    
         
             
                    self.uv: Dict[int, AgilentHPLCChromatogram] = {}
         
     | 
| 
       80 
89 
     | 
    
         
             
                    self.data_files: List = []
         
     | 
| 
       81 
90 
     | 
    
         | 
| 
      
 91 
     | 
    
         
            +
                def __new__(cls, *args, **kwargs):
         
     | 
| 
      
 92 
     | 
    
         
            +
                    if cls is RunController:
         
     | 
| 
      
 93 
     | 
    
         
            +
                        raise TypeError(f"only children of '{cls.__name__}' may be instantiated")
         
     | 
| 
      
 94 
     | 
    
         
            +
                    return object.__new__(cls)
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
       82 
96 
     | 
    
         
             
                @abc.abstractmethod
         
     | 
| 
       83 
     | 
    
         
            -
                def  
     | 
| 
      
 97 
     | 
    
         
            +
                def _fuzzy_match_most_recent_folder(
         
     | 
| 
      
 98 
     | 
    
         
            +
                    self, most_recent_folder: T, child_dirs: Set[str]
         
     | 
| 
      
 99 
     | 
    
         
            +
                ) -> Result[T, str]:
         
     | 
| 
       84 
100 
     | 
    
         
             
                    pass
         
     | 
| 
       85 
101 
     | 
    
         | 
| 
       86 
102 
     | 
    
         
             
                @abc.abstractmethod
         
     | 
| 
         @@ -120,6 +136,12 @@ class RunController(ABCTableController, abc.ABC): 
     | 
|
| 
       120 
136 
     | 
    
         | 
| 
       121 
137 
     | 
    
         
             
                def check_hplc_run_finished(self) -> Tuple[float, bool]:
         
     | 
| 
       122 
138 
     | 
    
         
             
                    if self.controller:
         
     | 
| 
      
 139 
     | 
    
         
            +
                        try:
         
     | 
| 
      
 140 
     | 
    
         
            +
                            _, current_run_file = self.get_current_run_data_dir_file()
         
     | 
| 
      
 141 
     | 
    
         
            +
                            sample_file, extension, _ = current_run_file.partition(".D")
         
     | 
| 
      
 142 
     | 
    
         
            +
                            self.current_run_child_files.add(sample_file)
         
     | 
| 
      
 143 
     | 
    
         
            +
                        except Exception:
         
     | 
| 
      
 144 
     | 
    
         
            +
                            pass
         
     | 
| 
       123 
145 
     | 
    
         
             
                        done_running = self.controller.check_if_not_running()
         
     | 
| 
       124 
146 
     | 
    
         
             
                        if self.curr_run_starting_time and self.timeout:
         
     | 
| 
       125 
147 
     | 
    
         
             
                            time_passed = time.time() - self.curr_run_starting_time
         
     | 
| 
         @@ -136,11 +158,11 @@ class RunController(ABCTableController, abc.ABC): 
     | 
|
| 
       136 
158 
     | 
    
         
             
                    raise ValueError("Controller is offline!")
         
     | 
| 
       137 
159 
     | 
    
         | 
| 
       138 
160 
     | 
    
         
             
                def check_hplc_done_running(self) -> Ok[T] | Err[str]:
         
     | 
| 
       139 
     | 
    
         
            -
                    """
         
     | 
| 
       140 
     | 
    
         
            -
                    Checks if ChemStation has finished running and can read data back
         
     | 
| 
      
 161 
     | 
    
         
            +
                    """Checks if ChemStation has finished running and can read data back
         
     | 
| 
       141 
162 
     | 
    
         | 
| 
       142 
163 
     | 
    
         
             
                    :return: Data file object containing most recent run file information.
         
     | 
| 
       143 
164 
     | 
    
         
             
                    """
         
     | 
| 
      
 165 
     | 
    
         
            +
                    self.current_run_child_files = set()
         
     | 
| 
       144 
166 
     | 
    
         
             
                    if self.timeout is not None:
         
     | 
| 
       145 
167 
     | 
    
         
             
                        finished_run = False
         
     | 
| 
       146 
168 
     | 
    
         
             
                        minutes = math.ceil(self.timeout / 60)
         
     | 
| 
         @@ -170,7 +192,9 @@ class RunController(ABCTableController, abc.ABC): 
     | 
|
| 
       170 
192 
     | 
    
         
             
                    else:
         
     | 
| 
       171 
193 
     | 
    
         
             
                        raise ValueError("Timeout value is None, no comparison can be made.")
         
     | 
| 
       172 
194 
     | 
    
         | 
| 
       173 
     | 
    
         
            -
                    check_folder = self. 
     | 
| 
      
 195 
     | 
    
         
            +
                    check_folder = self._fuzzy_match_most_recent_folder(
         
     | 
| 
      
 196 
     | 
    
         
            +
                        self.data_files[-1], self.current_run_child_files
         
     | 
| 
      
 197 
     | 
    
         
            +
                    )
         
     | 
| 
       174 
198 
     | 
    
         
             
                    if check_folder.is_ok() and finished_run:
         
     | 
| 
       175 
199 
     | 
    
         
             
                        return check_folder
         
     | 
| 
       176 
200 
     | 
    
         
             
                    elif check_folder.is_ok():
         
     | 
| 
         @@ -183,7 +207,7 @@ class RunController(ABCTableController, abc.ABC): 
     | 
|
| 
       183 
207 
     | 
    
         
             
                        except Exception:
         
     | 
| 
       184 
208 
     | 
    
         
             
                            self._reset_time()
         
     | 
| 
       185 
209 
     | 
    
         
             
                            return self.data_files[-1]
         
     | 
| 
       186 
     | 
    
         
            -
                    return Err("Run  
     | 
| 
      
 210 
     | 
    
         
            +
                    return Err("Run not may not have completed.")
         
     | 
| 
       187 
211 
     | 
    
         | 
| 
       188 
212 
     | 
    
         
             
                def get_uv_spectrum(self, path: str):
         
     | 
| 
       189 
213 
     | 
    
         
             
                    data_uv = rb.agilent.chemstation.parse_file(os.path.join(path, "DAD1.UV"))
         
     | 
| 
         @@ -212,9 +236,7 @@ class RunController(ABCTableController, abc.ABC): 
     | 
|
| 
       212 
236 
     | 
    
         
             
                    raise ValueError("Expected one of ReportType.TXT or ReportType.CSV")
         
     | 
| 
       213 
237 
     | 
    
         | 
| 
       214 
238 
     | 
    
         
             
                def get_spectrum_at_channels(self, data_path: str):
         
     | 
| 
       215 
     | 
    
         
            -
                    """
         
     | 
| 
       216 
     | 
    
         
            -
                    Load chromatogram for any channel in spectra dictionary.
         
     | 
| 
       217 
     | 
    
         
            -
                    """
         
     | 
| 
      
 239 
     | 
    
         
            +
                    """Load chromatogram for any channel in spectra dictionary."""
         
     | 
| 
       218 
240 
     | 
    
         
             
                    for channel, spec in self.spectra.items():
         
     | 
| 
       219 
241 
     | 
    
         
             
                        try:
         
     | 
| 
       220 
242 
     | 
    
         
             
                            spec.load_spectrum(data_path=data_path, channel=channel)
         
     | 
| 
         @@ -226,3 +248,16 @@ class RunController(ABCTableController, abc.ABC): 
     | 
|
| 
       226 
248 
     | 
    
         
             
                def _reset_time(self):
         
     | 
| 
       227 
249 
     | 
    
         
             
                    self.curr_run_starting_time = None
         
     | 
| 
       228 
250 
     | 
    
         
             
                    self.timeout = None
         
     | 
| 
      
 251 
     | 
    
         
            +
             
     | 
| 
      
 252 
     | 
    
         
            +
                def get_current_run_data_dir_file(self) -> Tuple[str, str]:
         
     | 
| 
      
 253 
     | 
    
         
            +
                    self.send(Command.GET_CURRENT_RUN_DATA_DIR)
         
     | 
| 
      
 254 
     | 
    
         
            +
                    full_path_name = self.receive()
         
     | 
| 
      
 255 
     | 
    
         
            +
                    self.send(Command.GET_CURRENT_RUN_DATA_FILE)
         
     | 
| 
      
 256 
     | 
    
         
            +
                    current_sample_file = self.receive()
         
     | 
| 
      
 257 
     | 
    
         
            +
                    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.")
         
     | 
| 
         @@ -7,33 +7,39 @@ Authors: Lucy Hao 
     | 
|
| 
       7 
7 
     | 
    
         
             
            from __future__ import annotations
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
            import abc
         
     | 
| 
       10 
     | 
    
         
            -
            import warnings
         
     | 
| 
       11 
10 
     | 
    
         
             
            from typing import Optional, Union
         
     | 
| 
       12 
11 
     | 
    
         | 
| 
       13 
12 
     | 
    
         
             
            from result import Err, Result
         
     | 
| 
       14 
13 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
            from  
     | 
| 
       16 
     | 
    
         
            -
            from  
     | 
| 
       17 
     | 
    
         
            -
            from  
     | 
| 
       18 
     | 
    
         
            -
            from  
     | 
| 
       19 
     | 
    
         
            -
            from  
     | 
| 
      
 14 
     | 
    
         
            +
            from ..macro import Command, Response
         
     | 
| 
      
 15 
     | 
    
         
            +
            from ..method_types import MethodDetails
         
     | 
| 
      
 16 
     | 
    
         
            +
            from ..sequence_types import SequenceTable
         
     | 
| 
      
 17 
     | 
    
         
            +
            from ..table_types import Table, RegisterFlag, TableOperation
         
     | 
| 
      
 18 
     | 
    
         
            +
            from ...control.controllers import CommunicationController
         
     | 
| 
       20 
19 
     | 
    
         | 
| 
       21 
20 
     | 
    
         
             
            TableType = Union[MethodDetails, SequenceTable]
         
     | 
| 
       22 
21 
     | 
    
         | 
| 
       23 
22 
     | 
    
         | 
| 
       24 
23 
     | 
    
         
             
            class ABCTableController(abc.ABC):
         
     | 
| 
      
 24 
     | 
    
         
            +
                """Abstract controller for all table-like objects in Chemstation.
         
     | 
| 
      
 25 
     | 
    
         
            +
                :param controller: controller for sending MACROs to Chemstation
         
     | 
| 
      
 26 
     | 
    
         
            +
                :param table: contains register keys needed for accessing table in Chemstation.
         
     | 
| 
      
 27 
     | 
    
         
            +
                """
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
       25 
29 
     | 
    
         
             
                def __init__(
         
     | 
| 
       26 
30 
     | 
    
         
             
                    self,
         
     | 
| 
       27 
31 
     | 
    
         
             
                    controller: Optional[CommunicationController],
         
     | 
| 
       28 
32 
     | 
    
         
             
                    table: Table,
         
     | 
| 
       29 
33 
     | 
    
         
             
                ):
         
     | 
| 
       30 
     | 
    
         
            -
                    warnings.warn(
         
     | 
| 
       31 
     | 
    
         
            -
                        "This abstract class is not meant to be initialized. Use MethodController or SequenceController."
         
     | 
| 
       32 
     | 
    
         
            -
                    )
         
     | 
| 
       33 
34 
     | 
    
         
             
                    self.controller = controller
         
     | 
| 
       34 
35 
     | 
    
         
             
                    self.table_locator = table
         
     | 
| 
       35 
36 
     | 
    
         
             
                    self.table_state: Optional[TableType] = None
         
     | 
| 
       36 
37 
     | 
    
         | 
| 
      
 38 
     | 
    
         
            +
                def __new__(cls, *args, **kwargs):
         
     | 
| 
      
 39 
     | 
    
         
            +
                    if cls is ABCTableController:
         
     | 
| 
      
 40 
     | 
    
         
            +
                        raise TypeError(f"only children of '{cls.__name__}' may be instantiated")
         
     | 
| 
      
 41 
     | 
    
         
            +
                    return object.__new__(cls, *args, **kwargs)
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
       37 
43 
     | 
    
         
             
                def receive(self) -> Result[Response, str]:
         
     | 
| 
       38 
44 
     | 
    
         
             
                    if self.controller:
         
     | 
| 
       39 
45 
     | 
    
         
             
                        for _ in range(10):
         
     | 
| 
         @@ -59,8 +65,7 @@ class ABCTableController(abc.ABC): 
     | 
|
| 
       59 
65 
     | 
    
         
             
                        raise ValueError("Controller is offline")
         
     | 
| 
       60 
66 
     | 
    
         | 
| 
       61 
67 
     | 
    
         
             
                def sleep(self, seconds: int):
         
     | 
| 
       62 
     | 
    
         
            -
                    """
         
     | 
| 
       63 
     | 
    
         
            -
                    Tells the HPLC to wait for a specified number of seconds.
         
     | 
| 
      
 68 
     | 
    
         
            +
                    """Tells the HPLC to wait for a specified number of seconds.
         
     | 
| 
       64 
69 
     | 
    
         | 
| 
       65 
70 
     | 
    
         
             
                    :param seconds: number of seconds to wait
         
     | 
| 
       66 
71 
     | 
    
         
             
                    """
         
     | 
| 
         @@ -93,6 +98,8 @@ class ABCTableController(abc.ABC): 
     | 
|
| 
       93 
98 
     | 
    
         
             
                        raise ValueError("Controller is offline")
         
     | 
| 
       94 
99 
     | 
    
         | 
| 
       95 
100 
     | 
    
         
             
                def add_new_col_num(self, col_name: RegisterFlag, val: Union[int, float]):
         
     | 
| 
      
 101 
     | 
    
         
            +
                    if not (isinstance(val, int) or isinstance(val, float)):
         
     | 
| 
      
 102 
     | 
    
         
            +
                        raise ValueError(f"{val} must be an int or float.")
         
     | 
| 
       96 
103 
     | 
    
         
             
                    self.sleepy_send(
         
     | 
| 
       97 
104 
     | 
    
         
             
                        TableOperation.NEW_COL_VAL.value.format(
         
     | 
| 
       98 
105 
     | 
    
         
             
                            register=self.table_locator.register,
         
     | 
| 
         @@ -103,6 +110,8 @@ class ABCTableController(abc.ABC): 
     | 
|
| 
       103 
110 
     | 
    
         
             
                    )
         
     | 
| 
       104 
111 
     | 
    
         | 
| 
       105 
112 
     | 
    
         
             
                def add_new_col_text(self, col_name: RegisterFlag, val: str):
         
     | 
| 
      
 113 
     | 
    
         
            +
                    if not isinstance(val, str):
         
     | 
| 
      
 114 
     | 
    
         
            +
                        raise ValueError(f"{val} must be a str.")
         
     | 
| 
       106 
115 
     | 
    
         
             
                    self.sleepy_send(
         
     | 
| 
       107 
116 
     | 
    
         
             
                        TableOperation.NEW_COL_TEXT.value.format(
         
     | 
| 
       108 
117 
     | 
    
         
             
                            register=self.table_locator.register,
         
     | 
| 
         @@ -115,10 +124,12 @@ class ABCTableController(abc.ABC): 
     | 
|
| 
       115 
124 
     | 
    
         
             
                def _edit_row_num(
         
     | 
| 
       116 
125 
     | 
    
         
             
                    self, col_name: RegisterFlag, val: Union[int, float], row: Optional[int] = None
         
     | 
| 
       117 
126 
     | 
    
         
             
                ):
         
     | 
| 
      
 127 
     | 
    
         
            +
                    if not (isinstance(val, int) or isinstance(val, float)):
         
     | 
| 
      
 128 
     | 
    
         
            +
                        raise ValueError(f"{val} must be an int or float.")
         
     | 
| 
       118 
129 
     | 
    
         
             
                    if row:
         
     | 
| 
       119 
130 
     | 
    
         
             
                        num_rows = self.get_num_rows()
         
     | 
| 
       120 
131 
     | 
    
         
             
                        if num_rows.is_ok():
         
     | 
| 
       121 
     | 
    
         
            -
                            if num_rows. 
     | 
| 
      
 132 
     | 
    
         
            +
                            if num_rows.ok_value.num_response < row:
         
     | 
| 
       122 
133 
     | 
    
         
             
                                raise ValueError("Not enough rows to edit!")
         
     | 
| 
       123 
134 
     | 
    
         | 
| 
       124 
135 
     | 
    
         
             
                    self.sleepy_send(
         
     | 
| 
         @@ -134,10 +145,12 @@ class ABCTableController(abc.ABC): 
     | 
|
| 
       134 
145 
     | 
    
         
             
                def _edit_row_text(
         
     | 
| 
       135 
146 
     | 
    
         
             
                    self, col_name: RegisterFlag, val: str, row: Optional[int] = None
         
     | 
| 
       136 
147 
     | 
    
         
             
                ):
         
     | 
| 
      
 148 
     | 
    
         
            +
                    if not isinstance(val, str):
         
     | 
| 
      
 149 
     | 
    
         
            +
                        raise ValueError(f"{val} must be a str.")
         
     | 
| 
       137 
150 
     | 
    
         
             
                    if row:
         
     | 
| 
       138 
151 
     | 
    
         
             
                        num_rows = self.get_num_rows()
         
     | 
| 
       139 
152 
     | 
    
         
             
                        if num_rows.is_ok():
         
     | 
| 
       140 
     | 
    
         
            -
                            if num_rows. 
     | 
| 
      
 153 
     | 
    
         
            +
                            if num_rows.ok_value.num_response < row:
         
     | 
| 
       141 
154 
     | 
    
         
             
                                raise ValueError("Not enough rows to edit!")
         
     | 
| 
       142 
155 
     | 
    
         | 
| 
       143 
156 
     | 
    
         
             
                    self.sleepy_send(
         
     | 
| 
         @@ -164,9 +177,7 @@ class ABCTableController(abc.ABC): 
     | 
|
| 
       164 
177 
     | 
    
         
             
                    )
         
     | 
| 
       165 
178 
     | 
    
         | 
| 
       166 
179 
     | 
    
         
             
                def add_row(self):
         
     | 
| 
       167 
     | 
    
         
            -
                    """
         
     | 
| 
       168 
     | 
    
         
            -
                    Adds a row to the provided table for currently loaded method or sequence.
         
     | 
| 
       169 
     | 
    
         
            -
                    """
         
     | 
| 
      
 180 
     | 
    
         
            +
                    """Adds a row to the provided table for currently loaded method or sequence."""
         
     | 
| 
       170 
181 
     | 
    
         
             
                    self.sleepy_send(
         
     | 
| 
       171 
182 
     | 
    
         
             
                        TableOperation.NEW_ROW.value.format(
         
     | 
| 
       172 
183 
     | 
    
         
             
                            register=self.table_locator.register, table_name=self.table_locator.name
         
     | 
| 
         @@ -174,9 +185,7 @@ class ABCTableController(abc.ABC): 
     | 
|
| 
       174 
185 
     | 
    
         
             
                    )
         
     | 
| 
       175 
186 
     | 
    
         | 
| 
       176 
187 
     | 
    
         
             
                def delete_table(self):
         
     | 
| 
       177 
     | 
    
         
            -
                    """
         
     | 
| 
       178 
     | 
    
         
            -
                    Deletes the table for the current loaded method or sequence.
         
     | 
| 
       179 
     | 
    
         
            -
                    """
         
     | 
| 
      
 188 
     | 
    
         
            +
                    """Deletes the table."""
         
     | 
| 
       180 
189 
     | 
    
         
             
                    self.sleepy_send(
         
     | 
| 
       181 
190 
     | 
    
         
             
                        TableOperation.DELETE_TABLE.value.format(
         
     | 
| 
       182 
191 
     | 
    
         
             
                            register=self.table_locator.register, table_name=self.table_locator.name
         
     | 
| 
         @@ -184,9 +193,7 @@ class ABCTableController(abc.ABC): 
     | 
|
| 
       184 
193 
     | 
    
         
             
                    )
         
     | 
| 
       185 
194 
     | 
    
         | 
| 
       186 
195 
     | 
    
         
             
                def new_table(self):
         
     | 
| 
       187 
     | 
    
         
            -
                    """
         
     | 
| 
       188 
     | 
    
         
            -
                    Creates the table for the currently loaded method or sequence.
         
     | 
| 
       189 
     | 
    
         
            -
                    """
         
     | 
| 
      
 196 
     | 
    
         
            +
                    """Creates the table."""
         
     | 
| 
       190 
197 
     | 
    
         
             
                    self.send(
         
     | 
| 
       191 
198 
     | 
    
         
             
                        TableOperation.CREATE_TABLE.value.format(
         
     | 
| 
       192 
199 
     | 
    
         
             
                            register=self.table_locator.register, table_name=self.table_locator.name
         
     |