control-lab-ly


Namecontrol-lab-ly JSON
Version 1.3.2 PyPI version JSON
download
home_pagehttps://github.com/kylejeanlewis/control-lab-le
SummaryLab Equipment Automation Package
upload_time2024-04-24 03:55:26
maintainerNone
docs_urlNone
authorChang Jie Leong
requires_python>=3.8
licenseMIT
keywords python lab automation
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Control.lab.ly
Lab Equipment Automation Package

## Description
User-friendly package that enables flexible automation an reconfigurable setups for high-throughput experimentation and machine learning.

## Package Structure
1. Compound
2. Control
3. Make
4. Measure
5. Move
6. Transfer
7. View

## Device support
- Make
  - (QInstruments) BioShake Orbital Shaker
  - (Arduino-based devices)
    - Multi-channel LED array
    - Multi-channel spin-coater
    - Peltier device
- Measure
  - (Keithley)
    - SMU 2450 Source Measure Unit Instrument
    - DAQ 6510 Data Acquisition and Multimeter System
  - (PiezoRobotics) Dynamic Mechanical Analyser (DMA)
  - (Sentron) SI series pH meters
  - (Arduino-based device) 
    - Precision mass balance
    - Load cell
- Move
  - (Creality) Ender-3
  - (Dobot) 
    - M1 Pro
    - MG400
  - (Arduino-based device) gantry robot running on GRBL
- Transfer
  - (Dobot) Gripper attachments
  - (Sartorius) rLINEĀ® dispensing modules
  - (TriContinent) C Series syringe pumps
  - (Arduino-based device) Peristaltic pump and syringe system
- View
  - (FLIR) AX8 thermal imaging camera
  - (General) Web cameras

## Installation
Control.lab.ly can be found on PyPI and can be easily installed with `pip install`.
```shell
$ pip install control-lab-ly
```

## Basic Usage
Simple start-up guide for basic usage of the package.

### Import desired class
```python
from controllably.Move.Cartesian import Ender
mover = Ender(...)
mover.safeMoveTo((x,y,z))
```

### View documentation
Use the built-in guide to read the documentation for the package.
```python
from controllably import guide_me
guide_me()
```
![Screenshot of the documentation guide](https://raw.githubusercontent.com/kylejeanlewis/repo-readme-assets/main/control-lab-le/v1-1-0/assets/Documentation%20guide.png)

Alternatively, details for each class / module / package can be explored by using the `help` function.
```python
help(Ender)
```

For basic usage, this is all you need to know. Check the documentation for more details on each class and function.

---

## Advanced Usage
For more advanced uses, Control.lab.ly provides a host of tools to streamline the development of lab equipment automation. This includes setting up configuration files and adding plugin drivers.

### Import package
```python
import controllably as lab
```

Optionally, you can set the safety policy for the session. This feature allows the user to prevent collisions before each movement is made. The safety policy has to be assigned before importing any of the `Mover` objects.
```python
lab.set_safety('high')  # Notifies; Pauses for input before every move action
lab.set_safety('low')   # Notifies; Waits for countdown before every move action
lab.set_safety(None)    # Carries out movement actions without delay

# Import control-lab-ly classes only after setting the safety policy
```

### Contents
1. [Setups](#1-creating-a-new-setup)
2. [Decks](#2-managing-a-deck)
3. [Addresses](#3-managing-project-addresses)
4. [Plugins](#4-using-plugins)


### 1. Creating a new setup
Create a `/configs/MySetup` folder that holds the configuration files for the setup, which includes `config.yaml` and `layout.json`.
```python
lab.create_setup(setup_name = "MySetup")
```

#### 1.1 `config.yaml`
This file stores the configuration and calibration values for your devices.
```yaml
MyDevice:                                       # device name
  module: Move                                  # top-level category
  class: Cartesian.Ender                        # device class
  settings:
    port: COM1                                  # serial port address
    setting_A: {'tuple': [300,0,200]}           # use keys to define the type of iterable
    setting_B: {'array': [[0,1,0],[-1,0,0]]}    # only tuple and np.array supported
```
> Each device configuration starts with the device name, then the following parameters:\
> `module`: top-level category (such as Make, Measure, Move,Transfer, View)\
> `class`: point to specific subclass using dot notation\
> `settings`: various initialisation settings\
> *See the* [**guide**](#view-documentation) *for more details on these parameters*

Compound devices are similarly configured. 
```yaml
MyCompoundDevice:                         # compound device name
  module: Compound
  class: LiquidMover.LiquidMoverSetup
  settings:                               # settings for your compound device
    setting_C: True
    component_config:                     # nest component configuration settings here
      MyFirstDevice:                      # component device name
        module: Transfer
        class: Liquid.SyringeAssembly
        settings:                         # settings for your component device
          port: COM22
          setting_D: 2                    
      MySecondDevice:                     # component device name
        module: Mover
        class: Jointed.Dobot.M1Pro
        settings:                         # settings for your compound device
          ip_address: '192.0.0.1'
```
> The configuration values for the component devices are nested under the `component_config` setting of the compound device.

Lastly, you can define shortcuts (or nicknames) to quickly access the components of compound devices.
```yaml
SHORTCUTS:
  First: 'MyCompoundDevice.MyFirstDevice'
  Second: 'MyCompoundDevice.MySecondDevice'
```
> A different serial port address or camera index may be used by different machines for the same device.\
*See* [**Section 3**](#3-creating-a-new-project) *to find out how to manage the different addresses used by different machines.*


#### 1.2 `layout.json`
This file stores the layout configuration of your physical workspace, also known as a `Deck`.\
*See* [**Section 2**](#2-managing-a-deck) *on how to load this information into the setup.*

*Optional: if your setup does not involve moving objects around in a pre-defined workspace,  a layout configuration may not be required*
> This package uses the same Labware files as those provided by [Opentrons](https://opentrons.com/), which can be found [here](https://labware.opentrons.com/), and custom Labware files can be created [here](https://labware.opentrons.com/create/). Labware files are JSON files that specifies the external and internal dimensions of a Labware block / object.


```json
{
  "reference_points":{
    "1": [11.1,22.2,33.3],
    "2": [44.4,55.5,66.6],
    "3": [77.7,88.8,99.9]
  },
  "slots":{
    "1": {
      "name": "MyLabware01",
      "exclusion_height": -1,
      "filepath": "MyREPO/.../MyLabware01.json"
    },
    "2": {
      "name": "MyLabware02",
      "exclusion_height": 0,
      "filepath": "MyREPO/.../MyLabware02.json"
    },
    "3": {
      "name": "MyLabware03",
      "exclusion_height": 10,
      "filepath": "MyREPO/.../MyLabware03.json"
    }
  }
}
```
> In `reference_points`, the bottom-left coordinates of each slot on the deck are defined. Slots are positions where Labware blocks may be placed.

> In `slots`, the `name` of the Labware and the `filepath` to the JSON file containing Labware details are defined. The filepath starts with the name of the repository's base folder.\
>\
> The `exclusion_height` is the height (in mm) above the dimensions of the Labware block to steer clear from when performing movement actions. Values less than 0 means the Labware is not avoided.\
>\
> *(Note: Labware avoidance only applies to final coordinates (i.e. destination). Does not guarantee collision avoidance when using point-to-point move actions. Use* `safeMoveTo()` *instead.)*

#### 1.3 Load setup
The initialisation of the setup occurs when importing `setup` from `configs.MySetup`. With `setup`, you can access all the devices that you have defined in [**Section 1.1**](#11-configyaml).

```python
### main.py ###
# Add repository folder to sys.path
from pathlib import Path
import sys
REPO = 'MyREPO'
ROOT = str(Path().absolute()).split(REPO)[0]
sys.path.append(f'{ROOT}{REPO}')

# Import the initialised setup
from configs.MySetup import setup
setup.MyDevice
setup.First
```


### 2. Managing a deck
*Optional: if your setup does not involve moving items around in a pre-defined workspace,  a* `Deck` *may not be required*

#### 2.1 Loading a deck
To load a `Deck` from the layout file, use the `loadDeck()` function of a `Mover` object (or its subclasses).
```python
from configs.MySetup import setup, LAYOUT_FILE
setup.Mover.loadDeck(LAYOUT_FILE)
``` 
> `LAYOUT_FILE` contains the details that has been defined in `layout.json` (see [**Section 1.2**](#12-layoutjson))

#### 2.2 Loading a Labware
To load a `Labware` onto the deck, use the `loadLabware()` method of the `Deck` object.
```python
setup.Mover.deck.loadLabware(...)
``` 
> This package uses the same Labware files as those provided by [Opentrons](https://opentrons.com/), which can be found [here](https://labware.opentrons.com/), and custom Labware files can be created [here](https://labware.opentrons.com/create/). Labware files are JSON files that specifies the external and internal dimensions of a Labware block / object.


### 3. Managing project addresses
A `/configs` folder will have been created in the base folder of your project repository to store all configuration related files from which the package will read from, in [**Section 1**](#1-creating-a-new-setup). A template of `registry.yaml` has also been added to the `/configs` folder to manage the machine-specific addresses of your connected devices (e.g. serial port and camera index).
```yaml
### registry.yaml ###
'012345678901234':              # insert your machine's 15-digit ID here
    cam_index:                  # camera index of the connected imaging devices
      __cam_01__: 1             # keep the leading and trailing double underscores
      __cam_02__: 0
    port:                       # addresses of serial ports
      __MyDevice__: COM1        # keep the leading and trailing double underscores
      __MyFirstDevice__: COM22
```

> Use the `Helper.get_node()` function to get the 15-digit unique identifier of your machine\
> Use the `Helper.get_port()` function to get the serial port addresses of your connect devices

```python
lab.Helper.get_node()           # Get your machine's ID (15-digit)
lab.Helper.get_ports()          # Get the port addresses of connected devices
```

Afterwards, change the value for the serial port address in the `config.yaml` file to match the registry.
```yaml
### config.yaml ###
MyDevice:
  module: Move
  class: Cartesian.Ender
  settings:
    port: __MyDevice__          # serial port address
```

### 4. Using plugins
*Optional: if drivers for your hardware components are already included, plugins may not be required*

User-defined plugins can be easily written and integrated into Control.lab.ly. All available classes and functions can be found in `lab.modules`.
```python
lab.guide_me()                              # Use guide to view imported objects
lab.modules.at.Make.Something.Good.MyClass  # Access the class 
```

#### 4.1 Registering a Class or Function
You can import the class and register the object using the `Factory.register()` function.
```python
from my_module import MyClass
lab.Factory.register(MyClass, "Make.Something.Good")
```

#### 4.2 Registering a Python module
Alternatively, you can automatically register all Classes and Functions in a Python module just by importing it.
> 1. Declare a `__where__` global variable to indicate where to register the module
> 2. At the end of the .py file, import and call  the `include_this_module()` function
```python
### my_module.py ###
__where__ = "Make.Something.Good"               # Where to register this module to

def MyClass:                                    # Main body of code goes here
  ...

def my_function:
  ...

from controllably import include_this_module    # Registers only the Classes and Functions
include_this_module()                           # defined above in this .py file
```

The Classes and Functions in the module will be registered when you import the module.
```python
### main.py ###
from my_module import MyClass, my_function
```
---

## Dependencies
- Markdown (>=3.3.4)
- numpy (>=1.19.5)
- opencv-python (>=4.5.4.58)
- pandas (>=1.2.4)
- pyModbusTCP (>=0.2.0)
- pyserial (>=3.5)
- PySimpleGUI (>=4.60.5)
- PyVISA (>=1.12.0)
- PyVISA-py (>=0.7)
- PyYAML (>=6.0)
- tkhtmlview (>=0.2.0)

## Contributors
[@kylejeanlewis](https://github.com/kylejeanlewis)\
[@mat-fox](https://github.com/mat-fox)\
[@Quijanove](https://github.com/Quijanove)\
[@AniketChitre](https://github.com/AniketChitre)


## How to Contribute
[Issues](https://github.com/kylejeanlewis/control-lab-le/issues) and feature requests are welcome!

## License
This project is distributed under the [MIT License](https://github.com/kylejeanlewis/control-lab-le/blob/main/LICENSE).

---
# Change Log

## Version 1.3.2
Feature enhancements, bug fixes and patches. First released 24 Apr 2024.
### Added
- add new `delay` parameter in `Keithley.programs.IV_Scan`
### Changed
- fix critical bug in setting sense/source limits for `KeithleyDevice`
- fix bugs in `KeithleyDevice`, `Peltier`, `ForceSensor`


## Version 1.3.1
Feature enhancements, bug fixes and patches. First released 11 Apr 2024.
### Added
- implementation of `TriContinent.pullback()`
- new `Well` properties and option in return list of wells by rows instead of columns
### Changed
- fix bugs in `Peltier` (`setTemperature()` and `getTemperature()`)
- fix bugs in `Ender` (`setTemperature()` and `getTemperature()`)
- fix bug in `Keithley.setFunction()`
- generalise `IV_Scan` to take either currents or voltages


## Version 1.3.0
Feature enhancements, bug fixes and patches. First released 19 Feb 2024.
### Added
- added check for poor physical connection with `PiezoRoboticsDevice`
- Keithley
  - added new subclasses of `KeithleyDevice`: `DAQ6510` and `SMU2450`
  - added way to read and save model name of `KeithleyDevice`
  - added new Keithley program for DAQ to scan multiple channels
  - new methods `clearErrors()`, `setDisplay()`, `setFunction()`
### Changed
- changed the way travel times are calculated for `Mover` tools, so that they reflect the actual physical travel times more accurately
- changed ability to delay initialisation of TriContinent pumps until it is in a more convenient location
- fixed few bugs with `SentronProbe` tool
### Removed
- removed old archived files


## Version 1.2.0
Feature enhancements, bug fixes and patches. First released 22 Aug 2023.
### Added
- `ForceClampSetup` class
- `LoadCell` class
- `Balance` class (subclass of `LoadCell`)
### Changed
- update `LEDArray` to delay timing loop by 0.1s
- fix bug with initialising `PiezoRoboticsDevice`
- update `getTemperature()` across multiple classes to standardise output
- `Mover` class
  - speed-related attributes and properties
  - add method to calculate travel time based on target speed, acceleration and deceleration
  - modify how speeds and max speeds interact with `move()` and `safeMoveTo()`
- `Cartesian` class
  - `setSpeed()` and `setSpeedFraction()`
  - get max speed settings from device upon connecting
  - change calculation of movement wait times using device speed and acceleration
- `Primitiv` class
  - change the class name to `Grbl` and `Primitiv` as a subclass name to retain compatibility
  - overload `moveTo()` and `_query()` methods to use jogging mode
  - modify the sequence of commands to halt movement
  - implement `getAcceleration()`, `getCoordinates()`, `getMaxSpeed()`
  - clear errors and setting feed rate upon establishing connection
- `Ender` class
  - change the class name to `Marlin` and `Ender` as a subclass name to retain compatibility
  - added method to immediately stop movement
  - implement `getAcceleration()`, `getCoordinates()`, `getMaxSpeed()`
  - separate methods for `setSpeed()` (absolute speed in mm/s) and `setSpeedFraction()` (proportional speed to max speed)
- `Dobot` class
  - added `stop()` method
- Flir `AX8` class
  - added `invertPalette()` method
  - added data parsing methods `_decode_from_modbus()` and `_encode_to_modbus()`
- `KeithleyDevice()` class
  - added `ip_address` property
  - added options for `_read()` method
  - added `readline()` method
  - implement `disconnect()` method
- fix bug with Keithley programs using `device.run()` instead of `device.start()`
### Removed
- `Thermal` class
- removed dependency on `imutils` package


## Versions 1.1.2 & 1.1.1
Bug fixes and patches. First released 12 Jul 2023.
### Added
- import `Device` classes in init files to view documentation
- added library for GRBL status and error codes
- add `update_root_direcctory()` function to Helper
### Changed
- fix bug with adding new rows into Dataframes
- use `reset_input_buffer()` instead of `flushInput()` for `pyserial.Serial` objects
- print the actual string sent to Serial devices
- update methods in `Deck`, `Labware`, and `Well` to camelCase
- update `Deck.isExcluded()` to apply strict inequalities when determining out-of-range coordinates
- update `LiquidMover` to insert a portion of tip into rack before ejecting
- update `Spinner`
  - fix bug with sending commands
  - added `_query()` method
  - pass verbosity to individual spinners
- verbosity of `Measure` objects pass through to devices
- update `PiezoRoboticsDevice`
  - initialize upon connection
  - raise errors when encountering them
- update `Mover`
  - modify`setFlag()` to print kwargs instead of raising error if assigned values are not boolean
  - use `safe_height` (if defined) instead of z-coordinate of home in `safeMoveTo()`
  - added `getSettings()` method
- update `Gantry` class
  - read multiple flines in `_query()`
  - check that commands end with newline before sending to device
  - fix bug with changing speeds
- update `Ender`
  - added `getTemperature()`, `holdTemperature()`, `isAtTemperature()` methods
  - modified `setTemperature()` to use Marlin code to wait for temperature
- update `Primitiv` class
  - add `getStatus()` and `stop()` methods
  - add `_get_settings()` method
- fix bug in `M1Pro.setHandedness()`
- update `Sartorius` class
  - `tip_inset_mm` now an instance attribute with initialisation parameters
  - set `tip_on` flag to False when performing `eject()`


## Version 1.1.0
Bug fixes and feature enhancements. First released 15 Jun 2023.
### Added
- `ForceSensor` - DIY force sensor (#55)
- `BioShake` - orbital shaker from QInstruments (#56)
- `SentronProbe` - pH meter probe from Sentron (#75)
- `Maker`
  - added `execute()` abstract method and implemented in subclasses
- GUI
  - `Guide` - documentation guide
  - `MakerPanel` - daptive GUI controls for `Maker` objects (#87)
### Changed
- `M1Pro`
  - fix issue with changing handedness (#86)
- `Peltier`
  - rename `getTemperatures()` to `getTemperature()`
  - rename `isReady()` to `isAtTemperature()`
  - rename `set_point` to `set_temperature`
- `Ender`
  - rename `set_point` to `set_temperature`
- `TriContinent`
  - rename `step_limit` to `limits`
- Refactor and reorganize `GUI` code
- Refactor code in `helper` and `factory`
- Updated documentation
### Removed
- `Analyse` sub-package removed
- `Control.Schedule` sub-package removed
- Unnecessary commented-out blocks of code


## Version 1.0.1
Bug fixes and minor feature enhancements. First released 08 May 2023.
### Added
- `LiquidMover`
  - Added `LiquidMover.touchTip()` method to touch the pipette tip against the walls of the vessel to remove excess liquid on the outside of the tip (#62)
  - Added option to indicate the position of the first available pipette tip in `LiquidMover` (#61)
- Added adaptive GUI controls for `Liquid` objects (#70)
- Added option to indicate which digital IO channel to use with Dobot attachments (#53)
### Changed
- `MassBalance`
  - Updated to the use `pd.concat()` instead of `pd.DataFrame.append()`, which is going ot be deprecated (#63)
  - Fixed endless loop for when `MassBalance` tries to `zero()` while recording data (#60)
- Changed the `Image` class and methods into functions within a module (#54)
- Fixed the tool offset of pipette when pipette tip is attached, and accounts for the length of pipette that enters the pipette tip (#64)
- Changed to using more precise time interval measurements by moving from `time.time()` to `time.perf_counter()` (#68)
- Fixed discrepancy in aspirate and dispense speed for `Sartorius` (#73) and let speed return to a global default value (#72)
- Updated documentation


## Version 1.0.0
Major overhaul in package structure. Standardisation of methods and consolidation of common methods. First released 12 Apr 2023.
### Added
- Usage of Abstract Base Classes (ABCs) to define a base class with abstract methods that needs to be implemented through sub-classing (#31)
- Usage of Protocols to provide an interface between different classes of objects (#30)
- Usage of Dataclasses to store complex data 
- Usage of decorators to modify methods
- Introduced different functions to parse the program docstring and find program parameters
### Changed
- Standardised methods and consolidated common methods
- Added type hints (#28)
- Moved Dobot attachments from Mover to Transfer.Substrate
- Split GUI Panels into individual files
- Split Dobot arms into individual files
- Split functions/methods in `misc.py` into individual files.
- Changed `_flags` to a public attribute `flags`
- Update documentation (#27, #28, #29)
### Removed
- Unnecessary commented-out blocks of code


## Version 0.0.x
(0.0.4.x) Introduced control for Peltier device and TriContinent Series C syringe pumps. First released 10 Mar 2023.\
(0.0.3.x) Minor changes to movement robot safety and pipette control. Introduced control for LED array. First released 08 Mar 2023.\
(0.0.2.x) Updates in setting up configuration files. First released 24 Feb 2023.\
(0.0.1.x) First release of [Control.lab.ly](https://pypi.org/project/control-lab-ly/) distributed on 23 Feb 2023.\
(0.0.0.x) Pre-release packaging checks
### Added
#### 0.0.4
- Added control for `Peltier` (#23)
  - set and get temperatures
  - hold temperatures for desired duration
  - checks if target temperature has been reached by checking power level lower than a threshold or time passed over a predefined duration, once the temperature is within tolerance
  - ability to record temperatures and timestamps 
- Added control for `TriContinent` and `TriContinentEnsemble` (#25)
  - single actions such as `empty`, `fill`, `initialise`, move actions, set speeds and valves, and wait
  - compound actions such as `aspirate`, `dispense`, and `prime`
#### 0.0.3
- Added safety measures for movement actions (#24)
  - In `Deck`, added exclusion zones when reading the `layout.json` file and new method `is_excluded()` to check if target coordinate is within the exclusion zone
  - In `Mover`, update `isFeasible()` method to check if target coordinates violates the deck's exclusion zone
  - New function `set_safety()` defines safety modes when starting a new session to pause for input (in "high" safety setting) and to wait for safety countdown (in "low" safety setting)
- `Make.Light.LEDArray` for controlling LEDs in the photo-reactor, as well as timing the LED "on" durations (#35)
#### 0.0.2.2
- Added import of `CompoundSetup` class
#### 0.0.2
- `Deck.at()` method for directly referencing slots using either index numbers or names
- New `CompoundSetup` class for common methods of `Compound` devices
- New `load_deck()` function to load `Deck` after initialisation
#### 0.0.1
- Make
  - Multi-channel spin-coater \[Arduino\]
- Measure
  - (Keithley) 2450 Source Measure Unit (SMU) Instrument
  - (PiezoRobotics) Dynamic Mechanical Analyser (DMA)
  - Precision mass balance \[Arduino\]
- Move
  - (Creality) Ender-3
  - (Dobot) M1 Pro
  - (Dobot) MG400
  - Primitiv \[Arduino\]
- Transfer
  - (Sartorius) rLINEĀ® dispensing modules
  - Peristaltic pump and syringe system \[Arduino\]
- View
  - (FLIR) AX8 thermal imaging camera - full functionality in development 
  - Web cameras \[General\] 
- misc
  - Helper class for most common actions
  - create_configs: make new directory for configuration files
  - create_setup: make new directory for specific setup-related files
  - load_setup: initialise setup on import during runtime

### Changed
#### 0.0.4
- Update documentation
#### 0.0.3.1
- Update documentation
#### 0.0.3
- `Sartorius`
  - made the blowout/home optional for the dispense method upon emptying the pipette
- Update documentation
#### 0.0.2.1
- Changed template files for `lab.create_setup()`
#### 0.0.2
- Update documentation

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/kylejeanlewis/control-lab-le",
    "name": "control-lab-ly",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "python, lab automation",
    "author": "Chang Jie Leong",
    "author_email": "changjie.leong@outlook.com",
    "download_url": "https://files.pythonhosted.org/packages/a9/b9/aa16d87000e778473eb584d2bc66de25c1380b87e6c3d2d9901a719442b6/control_lab_ly-1.3.2.tar.gz",
    "platform": null,
    "description": "# Control.lab.ly\r\nLab Equipment Automation Package\r\n\r\n## Description\r\nUser-friendly package that enables flexible automation an reconfigurable setups for high-throughput experimentation and machine learning.\r\n\r\n## Package Structure\r\n1. Compound\r\n2. Control\r\n3. Make\r\n4. Measure\r\n5. Move\r\n6. Transfer\r\n7. View\r\n\r\n## Device support\r\n- Make\r\n  - (QInstruments) BioShake Orbital Shaker\r\n  - (Arduino-based devices)\r\n    - Multi-channel LED array\r\n    - Multi-channel spin-coater\r\n    - Peltier device\r\n- Measure\r\n  - (Keithley)\r\n    - SMU 2450 Source Measure Unit Instrument\r\n    - DAQ 6510 Data Acquisition and Multimeter System\r\n  - (PiezoRobotics) Dynamic Mechanical Analyser (DMA)\r\n  - (Sentron) SI series pH meters\r\n  - (Arduino-based device) \r\n    - Precision mass balance\r\n    - Load cell\r\n- Move\r\n  - (Creality) Ender-3\r\n  - (Dobot) \r\n    - M1 Pro\r\n    - MG400\r\n  - (Arduino-based device) gantry robot running on GRBL\r\n- Transfer\r\n  - (Dobot) Gripper attachments\r\n  - (Sartorius) rLINE\u00ae dispensing modules\r\n  - (TriContinent) C Series syringe pumps\r\n  - (Arduino-based device) Peristaltic pump and syringe system\r\n- View\r\n  - (FLIR) AX8 thermal imaging camera\r\n  - (General) Web cameras\r\n\r\n## Installation\r\nControl.lab.ly can be found on PyPI and can be easily installed with `pip install`.\r\n```shell\r\n$ pip install control-lab-ly\r\n```\r\n\r\n## Basic Usage\r\nSimple start-up guide for basic usage of the package.\r\n\r\n### Import desired class\r\n```python\r\nfrom controllably.Move.Cartesian import Ender\r\nmover = Ender(...)\r\nmover.safeMoveTo((x,y,z))\r\n```\r\n\r\n### View documentation\r\nUse the built-in guide to read the documentation for the package.\r\n```python\r\nfrom controllably import guide_me\r\nguide_me()\r\n```\r\n![Screenshot of the documentation guide](https://raw.githubusercontent.com/kylejeanlewis/repo-readme-assets/main/control-lab-le/v1-1-0/assets/Documentation%20guide.png)\r\n\r\nAlternatively, details for each class / module / package can be explored by using the `help` function.\r\n```python\r\nhelp(Ender)\r\n```\r\n\r\nFor basic usage, this is all you need to know. Check the documentation for more details on each class and function.\r\n\r\n---\r\n\r\n## Advanced Usage\r\nFor more advanced uses, Control.lab.ly provides a host of tools to streamline the development of lab equipment automation. This includes setting up configuration files and adding plugin drivers.\r\n\r\n### Import package\r\n```python\r\nimport controllably as lab\r\n```\r\n\r\nOptionally, you can set the safety policy for the session. This feature allows the user to prevent collisions before each movement is made. The safety policy has to be assigned before importing any of the `Mover` objects.\r\n```python\r\nlab.set_safety('high')  # Notifies; Pauses for input before every move action\r\nlab.set_safety('low')   # Notifies; Waits for countdown before every move action\r\nlab.set_safety(None)    # Carries out movement actions without delay\r\n\r\n# Import control-lab-ly classes only after setting the safety policy\r\n```\r\n\r\n### Contents\r\n1. [Setups](#1-creating-a-new-setup)\r\n2. [Decks](#2-managing-a-deck)\r\n3. [Addresses](#3-managing-project-addresses)\r\n4. [Plugins](#4-using-plugins)\r\n\r\n\r\n### 1. Creating a new setup\r\nCreate a `/configs/MySetup` folder that holds the configuration files for the setup, which includes `config.yaml` and `layout.json`.\r\n```python\r\nlab.create_setup(setup_name = \"MySetup\")\r\n```\r\n\r\n#### 1.1 `config.yaml`\r\nThis file stores the configuration and calibration values for your devices.\r\n```yaml\r\nMyDevice:                                       # device name\r\n  module: Move                                  # top-level category\r\n  class: Cartesian.Ender                        # device class\r\n  settings:\r\n    port: COM1                                  # serial port address\r\n    setting_A: {'tuple': [300,0,200]}           # use keys to define the type of iterable\r\n    setting_B: {'array': [[0,1,0],[-1,0,0]]}    # only tuple and np.array supported\r\n```\r\n> Each device configuration starts with the device name, then the following parameters:\\\r\n> `module`: top-level category (such as Make, Measure, Move,Transfer, View)\\\r\n> `class`: point to specific subclass using dot notation\\\r\n> `settings`: various initialisation settings\\\r\n> *See the* [**guide**](#view-documentation) *for more details on these parameters*\r\n\r\nCompound devices are similarly configured. \r\n```yaml\r\nMyCompoundDevice:                         # compound device name\r\n  module: Compound\r\n  class: LiquidMover.LiquidMoverSetup\r\n  settings:                               # settings for your compound device\r\n    setting_C: True\r\n    component_config:                     # nest component configuration settings here\r\n      MyFirstDevice:                      # component device name\r\n        module: Transfer\r\n        class: Liquid.SyringeAssembly\r\n        settings:                         # settings for your component device\r\n          port: COM22\r\n          setting_D: 2                    \r\n      MySecondDevice:                     # component device name\r\n        module: Mover\r\n        class: Jointed.Dobot.M1Pro\r\n        settings:                         # settings for your compound device\r\n          ip_address: '192.0.0.1'\r\n```\r\n> The configuration values for the component devices are nested under the `component_config` setting of the compound device.\r\n\r\nLastly, you can define shortcuts (or nicknames) to quickly access the components of compound devices.\r\n```yaml\r\nSHORTCUTS:\r\n  First: 'MyCompoundDevice.MyFirstDevice'\r\n  Second: 'MyCompoundDevice.MySecondDevice'\r\n```\r\n> A different serial port address or camera index may be used by different machines for the same device.\\\r\n*See* [**Section 3**](#3-creating-a-new-project) *to find out how to manage the different addresses used by different machines.*\r\n\r\n\r\n#### 1.2 `layout.json`\r\nThis file stores the layout configuration of your physical workspace, also known as a `Deck`.\\\r\n*See* [**Section 2**](#2-managing-a-deck) *on how to load this information into the setup.*\r\n\r\n*Optional: if your setup does not involve moving objects around in a pre-defined workspace,  a layout configuration may not be required*\r\n> This package uses the same Labware files as those provided by [Opentrons](https://opentrons.com/), which can be found [here](https://labware.opentrons.com/), and custom Labware files can be created [here](https://labware.opentrons.com/create/). Labware files are JSON files that specifies the external and internal dimensions of a Labware block / object.\r\n\r\n\r\n```json\r\n{\r\n  \"reference_points\":{\r\n    \"1\": [11.1,22.2,33.3],\r\n    \"2\": [44.4,55.5,66.6],\r\n    \"3\": [77.7,88.8,99.9]\r\n  },\r\n  \"slots\":{\r\n    \"1\": {\r\n      \"name\": \"MyLabware01\",\r\n      \"exclusion_height\": -1,\r\n      \"filepath\": \"MyREPO/.../MyLabware01.json\"\r\n    },\r\n    \"2\": {\r\n      \"name\": \"MyLabware02\",\r\n      \"exclusion_height\": 0,\r\n      \"filepath\": \"MyREPO/.../MyLabware02.json\"\r\n    },\r\n    \"3\": {\r\n      \"name\": \"MyLabware03\",\r\n      \"exclusion_height\": 10,\r\n      \"filepath\": \"MyREPO/.../MyLabware03.json\"\r\n    }\r\n  }\r\n}\r\n```\r\n> In `reference_points`, the bottom-left coordinates of each slot on the deck are defined. Slots are positions where Labware blocks may be placed.\r\n\r\n> In `slots`, the `name` of the Labware and the `filepath` to the JSON file containing Labware details are defined. The filepath starts with the name of the repository's base folder.\\\r\n>\\\r\n> The `exclusion_height` is the height (in mm) above the dimensions of the Labware block to steer clear from when performing movement actions. Values less than 0 means the Labware is not avoided.\\\r\n>\\\r\n> *(Note: Labware avoidance only applies to final coordinates (i.e. destination). Does not guarantee collision avoidance when using point-to-point move actions. Use* `safeMoveTo()` *instead.)*\r\n\r\n#### 1.3 Load setup\r\nThe initialisation of the setup occurs when importing `setup` from `configs.MySetup`. With `setup`, you can access all the devices that you have defined in [**Section 1.1**](#11-configyaml).\r\n\r\n```python\r\n### main.py ###\r\n# Add repository folder to sys.path\r\nfrom pathlib import Path\r\nimport sys\r\nREPO = 'MyREPO'\r\nROOT = str(Path().absolute()).split(REPO)[0]\r\nsys.path.append(f'{ROOT}{REPO}')\r\n\r\n# Import the initialised setup\r\nfrom configs.MySetup import setup\r\nsetup.MyDevice\r\nsetup.First\r\n```\r\n\r\n\r\n### 2. Managing a deck\r\n*Optional: if your setup does not involve moving items around in a pre-defined workspace,  a* `Deck` *may not be required*\r\n\r\n#### 2.1 Loading a deck\r\nTo load a `Deck` from the layout file, use the `loadDeck()` function of a `Mover` object (or its subclasses).\r\n```python\r\nfrom configs.MySetup import setup, LAYOUT_FILE\r\nsetup.Mover.loadDeck(LAYOUT_FILE)\r\n``` \r\n> `LAYOUT_FILE` contains the details that has been defined in `layout.json` (see [**Section 1.2**](#12-layoutjson))\r\n\r\n#### 2.2 Loading a Labware\r\nTo load a `Labware` onto the deck, use the `loadLabware()` method of the `Deck` object.\r\n```python\r\nsetup.Mover.deck.loadLabware(...)\r\n``` \r\n> This package uses the same Labware files as those provided by [Opentrons](https://opentrons.com/), which can be found [here](https://labware.opentrons.com/), and custom Labware files can be created [here](https://labware.opentrons.com/create/). Labware files are JSON files that specifies the external and internal dimensions of a Labware block / object.\r\n\r\n\r\n### 3. Managing project addresses\r\nA `/configs` folder will have been created in the base folder of your project repository to store all configuration related files from which the package will read from, in [**Section 1**](#1-creating-a-new-setup). A template of `registry.yaml` has also been added to the `/configs` folder to manage the machine-specific addresses of your connected devices (e.g. serial port and camera index).\r\n```yaml\r\n### registry.yaml ###\r\n'012345678901234':              # insert your machine's 15-digit ID here\r\n    cam_index:                  # camera index of the connected imaging devices\r\n      __cam_01__: 1             # keep the leading and trailing double underscores\r\n      __cam_02__: 0\r\n    port:                       # addresses of serial ports\r\n      __MyDevice__: COM1        # keep the leading and trailing double underscores\r\n      __MyFirstDevice__: COM22\r\n```\r\n\r\n> Use the `Helper.get_node()` function to get the 15-digit unique identifier of your machine\\\r\n> Use the `Helper.get_port()` function to get the serial port addresses of your connect devices\r\n\r\n```python\r\nlab.Helper.get_node()           # Get your machine's ID (15-digit)\r\nlab.Helper.get_ports()          # Get the port addresses of connected devices\r\n```\r\n\r\nAfterwards, change the value for the serial port address in the `config.yaml` file to match the registry.\r\n```yaml\r\n### config.yaml ###\r\nMyDevice:\r\n  module: Move\r\n  class: Cartesian.Ender\r\n  settings:\r\n    port: __MyDevice__          # serial port address\r\n```\r\n\r\n### 4. Using plugins\r\n*Optional: if drivers for your hardware components are already included, plugins may not be required*\r\n\r\nUser-defined plugins can be easily written and integrated into Control.lab.ly. All available classes and functions can be found in `lab.modules`.\r\n```python\r\nlab.guide_me()                              # Use guide to view imported objects\r\nlab.modules.at.Make.Something.Good.MyClass  # Access the class \r\n```\r\n\r\n#### 4.1 Registering a Class or Function\r\nYou can import the class and register the object using the `Factory.register()` function.\r\n```python\r\nfrom my_module import MyClass\r\nlab.Factory.register(MyClass, \"Make.Something.Good\")\r\n```\r\n\r\n#### 4.2 Registering a Python module\r\nAlternatively, you can automatically register all Classes and Functions in a Python module just by importing it.\r\n> 1. Declare a `__where__` global variable to indicate where to register the module\r\n> 2. At the end of the .py file, import and call  the `include_this_module()` function\r\n```python\r\n### my_module.py ###\r\n__where__ = \"Make.Something.Good\"               # Where to register this module to\r\n\r\ndef MyClass:                                    # Main body of code goes here\r\n  ...\r\n\r\ndef my_function:\r\n  ...\r\n\r\nfrom controllably import include_this_module    # Registers only the Classes and Functions\r\ninclude_this_module()                           # defined above in this .py file\r\n```\r\n\r\nThe Classes and Functions in the module will be registered when you import the module.\r\n```python\r\n### main.py ###\r\nfrom my_module import MyClass, my_function\r\n```\r\n---\r\n\r\n## Dependencies\r\n- Markdown (>=3.3.4)\r\n- numpy (>=1.19.5)\r\n- opencv-python (>=4.5.4.58)\r\n- pandas (>=1.2.4)\r\n- pyModbusTCP (>=0.2.0)\r\n- pyserial (>=3.5)\r\n- PySimpleGUI (>=4.60.5)\r\n- PyVISA (>=1.12.0)\r\n- PyVISA-py (>=0.7)\r\n- PyYAML (>=6.0)\r\n- tkhtmlview (>=0.2.0)\r\n\r\n## Contributors\r\n[@kylejeanlewis](https://github.com/kylejeanlewis)\\\r\n[@mat-fox](https://github.com/mat-fox)\\\r\n[@Quijanove](https://github.com/Quijanove)\\\r\n[@AniketChitre](https://github.com/AniketChitre)\r\n\r\n\r\n## How to Contribute\r\n[Issues](https://github.com/kylejeanlewis/control-lab-le/issues) and feature requests are welcome!\r\n\r\n## License\r\nThis project is distributed under the [MIT License](https://github.com/kylejeanlewis/control-lab-le/blob/main/LICENSE).\r\n\r\n---\r\n# Change Log\r\n\r\n## Version 1.3.2\r\nFeature enhancements, bug fixes and patches. First released 24 Apr 2024.\r\n### Added\r\n- add new `delay` parameter in `Keithley.programs.IV_Scan`\r\n### Changed\r\n- fix critical bug in setting sense/source limits for `KeithleyDevice`\r\n- fix bugs in `KeithleyDevice`, `Peltier`, `ForceSensor`\r\n\r\n\r\n## Version 1.3.1\r\nFeature enhancements, bug fixes and patches. First released 11 Apr 2024.\r\n### Added\r\n- implementation of `TriContinent.pullback()`\r\n- new `Well` properties and option in return list of wells by rows instead of columns\r\n### Changed\r\n- fix bugs in `Peltier` (`setTemperature()` and `getTemperature()`)\r\n- fix bugs in `Ender` (`setTemperature()` and `getTemperature()`)\r\n- fix bug in `Keithley.setFunction()`\r\n- generalise `IV_Scan` to take either currents or voltages\r\n\r\n\r\n## Version 1.3.0\r\nFeature enhancements, bug fixes and patches. First released 19 Feb 2024.\r\n### Added\r\n- added check for poor physical connection with `PiezoRoboticsDevice`\r\n- Keithley\r\n  - added new subclasses of `KeithleyDevice`: `DAQ6510` and `SMU2450`\r\n  - added way to read and save model name of `KeithleyDevice`\r\n  - added new Keithley program for DAQ to scan multiple channels\r\n  - new methods `clearErrors()`, `setDisplay()`, `setFunction()`\r\n### Changed\r\n- changed the way travel times are calculated for `Mover` tools, so that they reflect the actual physical travel times more accurately\r\n- changed ability to delay initialisation of TriContinent pumps until it is in a more convenient location\r\n- fixed few bugs with `SentronProbe` tool\r\n### Removed\r\n- removed old archived files\r\n\r\n\r\n## Version 1.2.0\r\nFeature enhancements, bug fixes and patches. First released 22 Aug 2023.\r\n### Added\r\n- `ForceClampSetup` class\r\n- `LoadCell` class\r\n- `Balance` class (subclass of `LoadCell`)\r\n### Changed\r\n- update `LEDArray` to delay timing loop by 0.1s\r\n- fix bug with initialising `PiezoRoboticsDevice`\r\n- update `getTemperature()` across multiple classes to standardise output\r\n- `Mover` class\r\n  - speed-related attributes and properties\r\n  - add method to calculate travel time based on target speed, acceleration and deceleration\r\n  - modify how speeds and max speeds interact with `move()` and `safeMoveTo()`\r\n- `Cartesian` class\r\n  - `setSpeed()` and `setSpeedFraction()`\r\n  - get max speed settings from device upon connecting\r\n  - change calculation of movement wait times using device speed and acceleration\r\n- `Primitiv` class\r\n  - change the class name to `Grbl` and `Primitiv` as a subclass name to retain compatibility\r\n  - overload `moveTo()` and `_query()` methods to use jogging mode\r\n  - modify the sequence of commands to halt movement\r\n  - implement `getAcceleration()`, `getCoordinates()`, `getMaxSpeed()`\r\n  - clear errors and setting feed rate upon establishing connection\r\n- `Ender` class\r\n  - change the class name to `Marlin` and `Ender` as a subclass name to retain compatibility\r\n  - added method to immediately stop movement\r\n  - implement `getAcceleration()`, `getCoordinates()`, `getMaxSpeed()`\r\n  - separate methods for `setSpeed()` (absolute speed in mm/s) and `setSpeedFraction()` (proportional speed to max speed)\r\n- `Dobot` class\r\n  - added `stop()` method\r\n- Flir `AX8` class\r\n  - added `invertPalette()` method\r\n  - added data parsing methods `_decode_from_modbus()` and `_encode_to_modbus()`\r\n- `KeithleyDevice()` class\r\n  - added `ip_address` property\r\n  - added options for `_read()` method\r\n  - added `readline()` method\r\n  - implement `disconnect()` method\r\n- fix bug with Keithley programs using `device.run()` instead of `device.start()`\r\n### Removed\r\n- `Thermal` class\r\n- removed dependency on `imutils` package\r\n\r\n\r\n## Versions 1.1.2 & 1.1.1\r\nBug fixes and patches. First released 12 Jul 2023.\r\n### Added\r\n- import `Device` classes in init files to view documentation\r\n- added library for GRBL status and error codes\r\n- add `update_root_direcctory()` function to Helper\r\n### Changed\r\n- fix bug with adding new rows into Dataframes\r\n- use `reset_input_buffer()` instead of `flushInput()` for `pyserial.Serial` objects\r\n- print the actual string sent to Serial devices\r\n- update methods in `Deck`, `Labware`, and `Well` to camelCase\r\n- update `Deck.isExcluded()` to apply strict inequalities when determining out-of-range coordinates\r\n- update `LiquidMover` to insert a portion of tip into rack before ejecting\r\n- update `Spinner`\r\n  - fix bug with sending commands\r\n  - added `_query()` method\r\n  - pass verbosity to individual spinners\r\n- verbosity of `Measure` objects pass through to devices\r\n- update `PiezoRoboticsDevice`\r\n  - initialize upon connection\r\n  - raise errors when encountering them\r\n- update `Mover`\r\n  - modify`setFlag()` to print kwargs instead of raising error if assigned values are not boolean\r\n  - use `safe_height` (if defined) instead of z-coordinate of home in `safeMoveTo()`\r\n  - added `getSettings()` method\r\n- update `Gantry` class\r\n  - read multiple flines in `_query()`\r\n  - check that commands end with newline before sending to device\r\n  - fix bug with changing speeds\r\n- update `Ender`\r\n  - added `getTemperature()`, `holdTemperature()`, `isAtTemperature()` methods\r\n  - modified `setTemperature()` to use Marlin code to wait for temperature\r\n- update `Primitiv` class\r\n  - add `getStatus()` and `stop()` methods\r\n  - add `_get_settings()` method\r\n- fix bug in `M1Pro.setHandedness()`\r\n- update `Sartorius` class\r\n  - `tip_inset_mm` now an instance attribute with initialisation parameters\r\n  - set `tip_on` flag to False when performing `eject()`\r\n\r\n\r\n## Version 1.1.0\r\nBug fixes and feature enhancements. First released 15 Jun 2023.\r\n### Added\r\n- `ForceSensor` - DIY force sensor (#55)\r\n- `BioShake` - orbital shaker from QInstruments (#56)\r\n- `SentronProbe` - pH meter probe from Sentron (#75)\r\n- `Maker`\r\n  - added `execute()` abstract method and implemented in subclasses\r\n- GUI\r\n  - `Guide` - documentation guide\r\n  - `MakerPanel` - daptive GUI controls for `Maker` objects (#87)\r\n### Changed\r\n- `M1Pro`\r\n  - fix issue with changing handedness (#86)\r\n- `Peltier`\r\n  - rename `getTemperatures()` to `getTemperature()`\r\n  - rename `isReady()` to `isAtTemperature()`\r\n  - rename `set_point` to `set_temperature`\r\n- `Ender`\r\n  - rename `set_point` to `set_temperature`\r\n- `TriContinent`\r\n  - rename `step_limit` to `limits`\r\n- Refactor and reorganize `GUI` code\r\n- Refactor code in `helper` and `factory`\r\n- Updated documentation\r\n### Removed\r\n- `Analyse` sub-package removed\r\n- `Control.Schedule` sub-package removed\r\n- Unnecessary commented-out blocks of code\r\n\r\n\r\n## Version 1.0.1\r\nBug fixes and minor feature enhancements. First released 08 May 2023.\r\n### Added\r\n- `LiquidMover`\r\n  - Added `LiquidMover.touchTip()` method to touch the pipette tip against the walls of the vessel to remove excess liquid on the outside of the tip (#62)\r\n  - Added option to indicate the position of the first available pipette tip in `LiquidMover` (#61)\r\n- Added adaptive GUI controls for `Liquid` objects (#70)\r\n- Added option to indicate which digital IO channel to use with Dobot attachments (#53)\r\n### Changed\r\n- `MassBalance`\r\n  - Updated to the use `pd.concat()` instead of `pd.DataFrame.append()`, which is going ot be deprecated (#63)\r\n  - Fixed endless loop for when `MassBalance` tries to `zero()` while recording data (#60)\r\n- Changed the `Image` class and methods into functions within a module (#54)\r\n- Fixed the tool offset of pipette when pipette tip is attached, and accounts for the length of pipette that enters the pipette tip (#64)\r\n- Changed to using more precise time interval measurements by moving from `time.time()` to `time.perf_counter()` (#68)\r\n- Fixed discrepancy in aspirate and dispense speed for `Sartorius` (#73) and let speed return to a global default value (#72)\r\n- Updated documentation\r\n\r\n\r\n## Version 1.0.0\r\nMajor overhaul in package structure. Standardisation of methods and consolidation of common methods. First released 12 Apr 2023.\r\n### Added\r\n- Usage of Abstract Base Classes (ABCs) to define a base class with abstract methods that needs to be implemented through sub-classing (#31)\r\n- Usage of Protocols to provide an interface between different classes of objects (#30)\r\n- Usage of Dataclasses to store complex data \r\n- Usage of decorators to modify methods\r\n- Introduced different functions to parse the program docstring and find program parameters\r\n### Changed\r\n- Standardised methods and consolidated common methods\r\n- Added type hints (#28)\r\n- Moved Dobot attachments from Mover to Transfer.Substrate\r\n- Split GUI Panels into individual files\r\n- Split Dobot arms into individual files\r\n- Split functions/methods in `misc.py` into individual files.\r\n- Changed `_flags` to a public attribute `flags`\r\n- Update documentation (#27, #28, #29)\r\n### Removed\r\n- Unnecessary commented-out blocks of code\r\n\r\n\r\n## Version 0.0.x\r\n(0.0.4.x) Introduced control for Peltier device and TriContinent Series C syringe pumps. First released 10 Mar 2023.\\\r\n(0.0.3.x) Minor changes to movement robot safety and pipette control. Introduced control for LED array. First released 08 Mar 2023.\\\r\n(0.0.2.x) Updates in setting up configuration files. First released 24 Feb 2023.\\\r\n(0.0.1.x) First release of [Control.lab.ly](https://pypi.org/project/control-lab-ly/) distributed on 23 Feb 2023.\\\r\n(0.0.0.x) Pre-release packaging checks\r\n### Added\r\n#### 0.0.4\r\n- Added control for `Peltier` (#23)\r\n  - set and get temperatures\r\n  - hold temperatures for desired duration\r\n  - checks if target temperature has been reached by checking power level lower than a threshold or time passed over a predefined duration, once the temperature is within tolerance\r\n  - ability to record temperatures and timestamps \r\n- Added control for `TriContinent` and `TriContinentEnsemble` (#25)\r\n  - single actions such as `empty`, `fill`, `initialise`, move actions, set speeds and valves, and wait\r\n  - compound actions such as `aspirate`, `dispense`, and `prime`\r\n#### 0.0.3\r\n- Added safety measures for movement actions (#24)\r\n  - In `Deck`, added exclusion zones when reading the `layout.json` file and new method `is_excluded()` to check if target coordinate is within the exclusion zone\r\n  - In `Mover`, update `isFeasible()` method to check if target coordinates violates the deck's exclusion zone\r\n  - New function `set_safety()` defines safety modes when starting a new session to pause for input (in \"high\" safety setting) and to wait for safety countdown (in \"low\" safety setting)\r\n- `Make.Light.LEDArray` for controlling LEDs in the photo-reactor, as well as timing the LED \"on\" durations (#35)\r\n#### 0.0.2.2\r\n- Added import of `CompoundSetup` class\r\n#### 0.0.2\r\n- `Deck.at()` method for directly referencing slots using either index numbers or names\r\n- New `CompoundSetup` class for common methods of `Compound` devices\r\n- New `load_deck()` function to load `Deck` after initialisation\r\n#### 0.0.1\r\n- Make\r\n  - Multi-channel spin-coater \\[Arduino\\]\r\n- Measure\r\n  - (Keithley) 2450 Source Measure Unit (SMU) Instrument\r\n  - (PiezoRobotics) Dynamic Mechanical Analyser (DMA)\r\n  - Precision mass balance \\[Arduino\\]\r\n- Move\r\n  - (Creality) Ender-3\r\n  - (Dobot) M1 Pro\r\n  - (Dobot) MG400\r\n  - Primitiv \\[Arduino\\]\r\n- Transfer\r\n  - (Sartorius) rLINE\u00ae dispensing modules\r\n  - Peristaltic pump and syringe system \\[Arduino\\]\r\n- View\r\n  - (FLIR) AX8 thermal imaging camera - full functionality in development \r\n  - Web cameras \\[General\\] \r\n- misc\r\n  - Helper class for most common actions\r\n  - create_configs: make new directory for configuration files\r\n  - create_setup: make new directory for specific setup-related files\r\n  - load_setup: initialise setup on import during runtime\r\n\r\n### Changed\r\n#### 0.0.4\r\n- Update documentation\r\n#### 0.0.3.1\r\n- Update documentation\r\n#### 0.0.3\r\n- `Sartorius`\r\n  - made the blowout/home optional for the dispense method upon emptying the pipette\r\n- Update documentation\r\n#### 0.0.2.1\r\n- Changed template files for `lab.create_setup()`\r\n#### 0.0.2\r\n- Update documentation\r\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Lab Equipment Automation Package",
    "version": "1.3.2",
    "project_urls": {
        "Changelog": "https://github.com/kylejeanlewis/control-lab-le/blob/main/docs/CHANGELOG.md",
        "Documentation": "https://github.com/kylejeanlewis/control-lab-le/blob/main/docs/README.md",
        "GitHub": "https://github.com/kylejeanlewis/control-lab-le",
        "Homepage": "https://github.com/kylejeanlewis/control-lab-le",
        "Tracker": "https://github.com/kylejeanlewis/control-lab-le/issues"
    },
    "split_keywords": [
        "python",
        " lab automation"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "15b10d934bb6cf233050644eaab59d94d9cba51e9cd650fd7cd32ec83200f781",
                "md5": "1cd1e2c9222cf0c5e2328460b6414d30",
                "sha256": "150a54f1518c392796e1780662b8990642cfe7170df8d2b95852e15f63508338"
            },
            "downloads": -1,
            "filename": "control_lab_ly-1.3.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "1cd1e2c9222cf0c5e2328460b6414d30",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 381039,
            "upload_time": "2024-04-24T03:55:24",
            "upload_time_iso_8601": "2024-04-24T03:55:24.376361Z",
            "url": "https://files.pythonhosted.org/packages/15/b1/0d934bb6cf233050644eaab59d94d9cba51e9cd650fd7cd32ec83200f781/control_lab_ly-1.3.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "a9b9aa16d87000e778473eb584d2bc66de25c1380b87e6c3d2d9901a719442b6",
                "md5": "e59655b93b1dcb2a4b1c9dc255cad05e",
                "sha256": "4d67fdef7b5c106ee5c6fe168a1e514adb795e371ab35c4e1ea85c1c85bb3074"
            },
            "downloads": -1,
            "filename": "control_lab_ly-1.3.2.tar.gz",
            "has_sig": false,
            "md5_digest": "e59655b93b1dcb2a4b1c9dc255cad05e",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 530734,
            "upload_time": "2024-04-24T03:55:26",
            "upload_time_iso_8601": "2024-04-24T03:55:26.915848Z",
            "url": "https://files.pythonhosted.org/packages/a9/b9/aa16d87000e778473eb584d2bc66de25c1380b87e6c3d2d9901a719442b6/control_lab_ly-1.3.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-04-24 03:55:26",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "kylejeanlewis",
    "github_project": "control-lab-le",
    "github_not_found": true,
    "lcname": "control-lab-ly"
}
        
Elapsed time: 0.22507s