Source code for AFL.automation.loading.PushPullSelectorSampleCell

from AFL.automation.loading.SampleCell import SampleCell
from AFL.automation.loading.Tubing import Tubing
from AFL.automation.APIServer.Driver import Driver
from collections import defaultdict
import time

import math

[docs] class PushPullSelectorSampleCell(Driver,SampleCell): ''' Class for a sample cell consisting of a pump and a one-to-many flow selector where the pump line holds sample (pulling and pushing as necessary) with a cell on a separate selector channel (in contrast to an inline selector cell where the cell is in the pump line). @TODO: write support for multiple cells on separate channels (up to 6 cells on a 10-position selector) @TODO: figure out when/where to pull in air to the syringe to make up for extra_vol_to_empty_ml '''
[docs] def __init__(self,pump, selector, ncells=1, thickness=None, catch_to_sel_vol=None, cell_to_sel_vol=None, syringe_to_sel_vol=None, selector_internal_vol=None, calibrated_catch_to_syringe_vol=None, calibrated_syringe_to_cell_vol=None, rinse_speed=50.0, load_speed=10.0, rinse_flow_delay=3.0, load_flow_delay=10.0, ): ''' ncells = number of connected cells (up to 6 cells with a 10-position flow selector, with four positions taken by load port, rinse, waste, and air) Name = the cell name, array with length = ncells thickness = cell path length, to be incorporated into metadata, array with length = ncells cell state if not 'clean', array with length = ncells pump: a pump object supporting withdraw() and dispense() methods e.g. pump = NE1KSyringePump(port,syringe_id_mm,syringe_volume) selector: a selector object supporting string-based selectPort() method with options 'catch','cell','rinse','waste','air' e.g. selector = ViciMultiposSelector(port,portlabels={'catch':1,'cell':2,'rinse':3,'waste':4,'air':5}) ''' self._app = None self.name = 'PushPullSelectorSampleCell' self.pump = pump self.selector = selector self.cell_state = defaultdict(lambda: 'clean') if catch_to_sel_vol is None: self.catch_to_selector_vol = Tubing(1530,2.2*52).volume() + Tubing(1,25.4).volume() + 2.0 else: self.catch_to_selector_vol = catch_to_sel_vol if cell_to_sel_vol is None: self.cell_to_selector_vol = Tubing(1517,262.9).volume() + 1 else: self.cell_to_selector_vol = cell_to_sel_vol if syringe_to_sel_vol is None: self.syringe_to_selector_vol = Tubing(1530,49.27).volume() else: self.syringe_to_selector_vol = syringe_to_sel_vol if selector_internal_vol is None: self.selector_internal_vol = Tubing(1529,1).volume() else: self.selector_internal_vol = selector_internal_vol self.calibrated_catch_to_syringe_vol = calibrated_catch_to_syringe_vol self.calibrated_syringe_to_cell_vol = calibrated_syringe_to_cell_vol self.catch_empty_ffvol = 2 self.to_waste_vol = 1 self.rinse_prime_vol = 3 self.rinse_vol_ml = 3 self.blow_out_vol = 6 self.nrinses_cell_flood = 2 self.nrinses_syringe = 2 self.nrinses_cell = 1 self.nrinses_catch = 2 self.syringe_dirty = False self.rinse_speed = rinse_speed self.load_speed = load_speed self.rinse_flow_delay = rinse_flow_delay self.load_flow_delay = load_flow_delay self.pump.setRate(self.rinse_speed) #variables that we'll allow users to set via the API self.remote_parameters = [ 'calibrated_catch_to_syringe_vol', 'calibrated_syringe_to_cell_vol', 'catch_to_selector_vol', 'cell_to_selector_vol', 'syringe_to_selector_vol', 'selector_internal_vol', 'catch_empty_ffvol', 'to_waste_vol', 'rinse_prime_vol', 'rinse_vol_ml', 'blow_out_vol', 'nrinses_cell_flood', 'nrinses_syringe', 'nrinses_cell', 'nrinses_catch', 'syringe_dirty', 'rinse_speed', 'load_speed', 'rinse_flow_delay', 'load_flow_delay', ]
@property def app(self): return self._app @app.setter def app(self,app): self._app = app self.pump.app = app self.selector.app = app
[docs] def status(self): status = [] status.append(f'CellState: {dict(self.cell_state)}') status.append(f'SelectorState: {self.selector.portString}') status.append(f'Pump: {self.pump.name}') status.append(f'Selector: {self.selector.name}') status.append(f'Cell: {self.name}') for k,v in self.selector.portlabels.items(): status.append(f'Port {v}: {k}') return status
[docs] def setParameter(self,parameter,value): if parameter not in self.remote_parameters: raise ValueError(f'Parameter {parameter} not settable') else: setattr(self,parameter,value)
[docs] def getParameters(self): for parameter in self.remote_parameters: value = getattr(self,parameter) print(f'{parameter:30s} = {value}')
[docs] def transfer(self,source,dest,vol_source,vol_dest=None): if vol_dest is None: vol_dest = vol_source self.app.logger.debug(f'Transferring {vol_source}mL from {source} and {vol_dest}mL to {dest}') if vol_dest>vol_source: self.app.logger.debug(f'Withrawing {vol_dest-vol_source} mL from air') self.selector.selectPort('air') self.pump.withdraw(vol_dest-vol_source) self.selector.selectPort(source) self.pump.withdraw(vol_source) self.selector.selectPort(dest) self.pump.dispense(vol_dest) if vol_dest<vol_source: self.app.logger.debug(f'Dumping {vol_source-vol_dest} mL excess to waste') self.selector.selectPort('waste') self.pump.dispense(vol_source-vol_dest)
[docs] def catchToSyringe(self,sampleVolume=0): self.pump.setRate(self.load_speed) self.pump.flow_delay = self.load_flow_delay vol_source = self.catch_to_selector_vol+self.syringe_to_selector_vol+self.catch_empty_ffvol + sampleVolume self.selector.selectPort('catch') self.pump.withdraw(vol_source)
[docs] def loadSample(self,cellname='cell',sampleVolume=0): if self.syringe_dirty: self.rinseSyringe() if not self.cell_state[cellname] =='clean': self.rinseCell(cellname=cellname) self.pump.setRate(self.load_speed) self.pump.flow_delay = self.load_flow_delay if (self.calibrated_catch_to_syringe_vol is None) or (self.calibrated_catch_to_syringe_vol=='None'): vol_source = self.catch_to_selector_vol vol_source += self.syringe_to_selector_vol vol_source +=self.catch_empty_ffvol else: vol_source = self.calibrated_catch_to_syringe_vol if (self.calibrated_syringe_to_cell_vol is None) or (self.calibrated_syringe_to_cell_vol=='None'): vol_dest = self.syringe_to_selector_vol vol_dest += self.cell_to_selector_vol else: vol_dest = self.calibrated_syringe_to_cell_vol vol_dest += sampleVolume vol_source += sampleVolume self.transfer('catch',cellname,vol_source,vol_dest) # self.selector.selectPort('catch') # self.pump.withdraw(self.catch_to_selector_vol+self.syringe_to_selector_vol+self.catch_empty_ffvol) # self.selector.selectPort(cellname) # self.pump.dispense(self.syringe_to_selector_vol + self.cell_to_selector_vol) self.cell_state[cellname] = 'loaded' self.pump.setRate(self.rinse_speed) self.pump.flow_delay = self.rinse_flow_delay
def _firstCleanCell(self,rinse_if_none=False): # find the first clean cell and use that. for (cn,ct,cs) in zip(self.name,self.thickness,self.state): if cs is 'clean': return (cn,ct,cs) if clean_if_none: for (cn,ct,cs) in zip(self.name,self.thickness,self.state): if cs is 'dirty': self.rinseCell(cellname=cn) return (cn,ct,cs)
[docs] def catchToWaste(self,sampleVolume=0.0): if (self.calibrated_catch_to_syringe_vol is None) or (self.calibrated_catch_to_syringe_vol=='None'): vol_source = self.syringe_to_selector_vol vol_source +=self.catch_empty_ffvol vol_source += sampleVolume else: vol_source = self.calibrated_catch_to_syringe_vol vol_source += sampleVolume vol_dest = self.syringe_to_selector_vol + self.to_waste_vol + sampleVolume self.transfer('catch','waste',vol_source,vol_dest) self.syringe_dirty = True
[docs] def cellToWaste(self,cellname='cell'): self.transfer(cellname,'waste',self.cell_to_selector_vol + self.syringe_to_selector_vol,vol_dest = self.syringe_to_selector_vol + self.to_waste_vol) self.syringe_dirty = True
[docs] def rinseSyringe(self): self.pump.setRate(self.rinse_speed) self.pump.flow_delay = self.rinse_flow_delay for i in range(self.nrinses_syringe): self.transfer('rinse','waste',self.rinse_vol_ml) self.syringe_dirty = False
[docs] def rinseCell(self,cellname='cell'): self.pump.setRate(self.rinse_speed) self.pump.flow_delay = self.rinse_flow_delay self.rinseCellFlood(cellname)
[docs] def rinseCellPull(self,cellname = 'cell'): self.pump.setRate(self.rinse_speed) self.pump.flow_delay = self.rinse_flow_delay #rinse the cell for i in range(self.nrinses_cell): self.selector.selectPort('rinse') self.pump.withdraw(self.rinse_vol_ml) self.selector.selectPort(cellname) for i in range(3): #swish the fluid around in the cell self.pump.dispense(self.rinse_vol_ml) self.pump.withdraw(self.rinse_vol_ml) self.selector.selectPort('waste') self.pump.dispense(self.rinse_vol_ml + self.to_waste_vol) self.syringe_dirty = True
[docs] def rinseCellFlood(self,cellname='cell'): self.pump.setRate(self.rinse_speed) self.pump.flow_delay = self.rinse_flow_delay if self.syringe_dirty: self.rinseSyringe() self.blowOutCell(cellname) for i in range(self.nrinses_cell_flood): self.transfer('rinse',cellname,self.rinse_vol_ml) self.blowOutCell(cellname) self.cell_state[cellname] = 'clean'
[docs] def swish(self,vol): self.pump.setRate(self.rinse_speed) self.pump.flow_delay = self.rinse_flow_delay self.pump.withdraw(vol) self.pump.dispense(vol)
[docs] def blowOutCell(self,cellname='cell'): self.pump.setRate(self.rinse_speed) self.pump.flow_delay = self.rinse_flow_delay self.selector.selectPort('air') self.pump.withdraw(self.blow_out_vol) self.selector.selectPort(cellname) self.pump.dispense(self.blow_out_vol)
[docs] def blowOutCellForcedAir(self,cellname='cell',waittime=20): self.selector.selectPort('cell') self.selector.selectPort('forced_air_cell') time.sleep(waittime) self.selector.selectPort('cell')
[docs] def rinseCatch(self): self.pump.setRate(self.rinse_speed) self.pump.flow_delay = self.rinse_flow_delay for i in range(self.nrinses_catch): from_vol = self.rinse_vol_ml + self.syringe_to_selector_vol if (self.calibrated_catch_to_syringe_vol is None) or (self.calibrated_catch_to_syringe_vol=='None'): to_vol = self.rinse_vol_ml + self.catch_to_selector_vol else: to_vol = self.calibrated_catch_to_syringe_vol + self.rinse_vol_ml self.transfer('rinse','catch',from_vol,to_vol) for i in range(1): self.swish(self.rinse_vol_ml) if (self.calibrated_catch_to_syringe_vol is None) or (self.calibrated_catch_to_syringe_vol=='None'): from_vol = self.rinse_vol_ml + self.catch_to_selector_vol + self.catch_empty_ffvol else: from_vol = self.calibrated_catch_to_syringe_vol + self.catch_empty_ffvol + self.rinse_vol_ml to_vol = self.rinse_vol_ml + self.syringe_to_selector_vol self.transfer('catch','waste',from_vol,to_vol) #clear out any remaining volume in the syringe self.selector.selectPort('waste') self.pump.emptySyringe()
[docs] def rinseAll(self,cellname='cell'): # if self.state is 'loaded': # self.cellToWaste() self.rinseCell(cellname=cellname) self.rinseCatch() self.rinseSyringe()