Examples

Here you will find an in-depth example of using the Pycont library.

Pump Usage Example

This is an example of how to control a Tricontinent pump, using the Pycont library.

Below are samples of a .json configuration file which holds the parameters for the pump, and the python script which will control the pump.

JSON Configuration File (pump_setup_config.json)

This is the structure of a .json configuration file.

  • io

    This is the I/O port which will communicate with the pump from the host PC. Below is an example of communicating on a UNIX based system.

    • baudrate

      This is the communication baudrate for serial communication. This is usually set to 9600

    • timeout

      The default time to wait until the communication times out. (This is repeated several times)

  • default

    These are the default setting for all the pumps on the line. Here is where you set parameters, such as speed and volume.

    • volume

      The maximum volume available to pump

    • micro_step_mode

      This is the default microstep mode for the pumps. Mode 2 should suffice.

    • top_velocity

      This is the top speed for all the pumps.

    • initialize_valve_position

      The default position for the 3/4-way valve on top of the pump.

  • groups

    These are the collection of pumps connected on the line. Here, they are named after the chemicals which they hold.

  • pumps

    This is where we can set the individual settings for each pump. Each pump listed in the “group” section, must have it’s “switch” position initialised here.

    • switch

      The switch setting on the back of the pump. This identifies each pump on the line, hardware-wise.

JSON configuration file example:

{
    "io":
    {
        "port": "/dev/ttyUSB0",
        "baudrate": 9600,
        "timeout": 1
    },
    "default":
    {
        "volume": 5,
        "micro_step_mode": 2,
        "top_velocity": 5000,
        "initialize_valve_position": "I"
    },
    "groups":
    {
        "chemicals": ["acetone", "water"]
    },
    "pumps":
    {
        "acetone":
        {
            "switch": "0"
        },
        "water":
        {
            "switch": "1"
        }
    }
}

Python Script (pycont_test.py)

This is an example of a Python script which uses Pycont to control a set of pumps.

The steps are easy:

  1. First, import the appropriate module. Here, it is: ‘pycont.controller’

  2. Load the .json configuration file in a Controller object, e.g. ‘MultiPumpController’

  3. Smart initialize the pumps with ‘smart_initialize()’ function.

  4. The pumps are now initialized and ready for use!

  5. The pumps can be accessed in two ways:

    • ‘controller.pumps[‘pump_name’]’
    • ‘controller.pump_name’
    • These are personal preference, use whichever you like.
  6. Have fun!

Python Script Example:

# simply import the module
import pycont.controller

# link to your config file
SETUP_CONFIG_FILE = './pump_setup_config.json'

# and load the config file in a MultiPumpController
controller = pycont.controller.MultiPumpController.from_configfile(SETUP_CONFIG_FILE)

# initialize the pumps in a smart way, if they are already initialized we do not want to reinitialize them because they got back to zero position
controller.smart_initialize()

# individual pumps can be accessed in two ways:
# - in the dict ```controller.pumps['pump_name']```
# - directly as an attribute ```controller.pump_name```
# the two above method link to the same pump instance
# we use the first convention because it highlight well the name of the pumps
# the second convention is certainly more convenient for online testing using ipython

# ask a pump to go to a specific position, calling it by its name
# the wait argument signifies if the command is blocking or non-blocking
# if wait=False (default), the function returns immediately and let you go on
# volumes are always in mL
controller.pumps['acetone'].go_to_volume(0.5, wait=False)
# if wait=True, the function returns only after the pump finished his move
controller.pumps['water'].go_to_volume(0.5, wait=True)

# of course you can pump and deliver volumes
controller.pumps['water'].pump(0.5, wait=True)
controller.pumps['water'].deliver(0.5, wait=True)

# and those function tells you is the action what feasible or not
succeed = controller.pumps['water'].pump(1000, wait=True)
if succeed:
    print('How could you pump 1000 mL')
else:
    print('You cannot pump 1000 mL!')

# the pump and deliver function respectively have a from_valve and to_valve argument
# if set, the valve position is set before the pump moves
controller.pumps['water'].pump(0.5, from_valve=pycont.controller.VALVE_INPUT, wait=True)
controller.pumps['water'].deliver(0.5, to_valve=pycont.controller.VALVE_OUTPUT, wait=True)

# you can also transfer volume from valve to valve
# the function is recusive so even of the volume is bigger than the syringe, it will iterate as many times as needed
controller.pumps['acetone'].transfer(7, pycont.controller.VALVE_INPUT, pycont.controller.VALVE_OUTPUT)  # this function is blocking, no wait argument
# note that it pump from and to the position it is currently set to, made it easy to leave a small volume in the pump if needed

# you can also iterate on all the pumps
for _, pump in controller.pumps.items():
    pump.go_to_volume(0)  # here wait=False by default, all pumps move in parrallel
# wait until all pumps are ready to operate again
controller.wait_until_all_pumps_idle()

# you can apply command to all pumps in parrallel, in one command!
# this is the purpose of the controller.apply_command_to_all_pumps
# let's have the pumps go to their max volume
# the below function go through the list of pumps and run the 'go_to_max_volume' function without argument
controller.apply_command_to_all_pumps('go_to_max_volume')
while controller.are_pumps_busy():
# and record the volume in real time as the pumps are moving
    print(controller.apply_command_to_all_pumps('get_volume'))

# and you set pump group in the config file and apply command to a group of pumps
# check the config file for group definition
# in this example 'chemicals' contains ['water', 'acetone']
controller.apply_command_to_group('chemicals', 'go_to_volume', 1)
controller.wait_until_all_pumps_idle()

# the two above function call the more generic apply_command_to_pumps function
# which take a list of pumps to apply the command to
controller.apply_command_to_pumps(['water', 'acetone'], 'go_to_volume', 1.5)
controller.wait_until_all_pumps_idle()

# So the three above way are different way to do the same things
# groups are a powerful way to automate initialization of your setup

time.sleep(1)  # just to pause so that you can hear the sound of valve movements

# of course you can change valve position
# for this you should use the command set_valve_position(valve_position) using for valvle position the global variable define in pycont. They are VALVE_INPUT, VALVE_OUTPUT, VALVE_BYPASS, VALVE_EXTRA
controller.pumps['acetone'].set_valve_position(pycont.controller.VALVE_OUTPUT)
controller.pumps['water'].set_valve_position(pycont.controller.VALVE_OUTPUT)

time.sleep(1)  # just to pause so that you can hear the sound of valve movements

# of course you can change all the valve position at once
# apply_command_to_all_pumps will forward all additional argument
controller.apply_command_to_all_pumps('set_valve_position', pycont.controller.VALVE_INPUT)

# get valve position
print(controller.pumps['water'].get_valve_position())
print(controller.apply_command_to_all_pumps('get_valve_position'))

# and compare it with global defined variable
if controller.pumps['water'].get_valve_position() == pycont.controller.VALVE_INPUT:
    print('The valve for water is indeed in input position')
else:
    print('Something went wrong when setting the valve position')


# finally there is some tools to track the status of the pumps
print(controller.pumps['water'].is_idle())  # is the pump ready?
print(controller.pumps['water'].is_busy())  # is the pump busy?
print(controller.pumps['water'].current_volume)  # what volume is in the syringe, this is a direct reading from the pump position, we actually ask the pump!
print(controller.pumps['water'].remaining_volume)  # what volume can still be pump
print(controller.pumps['water'].is_volume_pumpable(1))  # can I pump 1 ml?
print(controller.pumps['water'].is_volume_deliverable(1))  # can I deliver 1 ml?

# But note that the above tools are mostly encompassed in the higher level functions such as controller.wait_until_all_pumps_idle() which check is_idle() for all pumps

# Have fun!

Note

Insert more examples here!