# Copyright (C) 2014-2019, Ariel Vina Rodriguez ( arielvina@yahoo.es )
# distributed under the GNU General Public License, see <http://www.gnu.org/licenses/>.
#
# author Ariel Vina-Rodriguez (qPCR4vir)
# 2014-2019
"""
Worktable and labwares
======================
- :py:class:`Worktable`
- :py:class:`Worktable.Location`
- :py:class:`Labware.Type`
+ Specialized types:
+ :py:class:`DiTiRackType`
+ :py:class:`CuvetteType`
- :py:class:`Labware.Type.Series`
- :py:class:`Labware`
+ Specialized labwares:
+ :py:class:`DitiRack`
+ :py:class:`Cuvette`
---------
"""
__author__ = 'qPCR4vir'
from pathlib import Path
import logging
import xml.etree.ElementTree as ET
base_dir = Path(__file__).parent.parent
[docs]class LiquidClass:
[docs] def __init__(self, name:str, liquid_name:str= ""):
"""
:param name:
:param liquid_name:
"""
self.name = name
self.liquid_name = liquid_name
def __str__(self):
return self.name
[docs]class LiquidClassDefault(LiquidClass):
[docs] def __init__(self, name:str, liquid_name:str= ""):
LiquidClass.__init__(self, name, liquid_name)
self.derived = {}
[docs]class LiquidClassDerived(LiquidClass):
[docs] def __init__(self, raw_name:str, origen:LiquidClassDefault):
raw = raw_name.split(';')
[docs]class LiquidClasses:
[docs] def __init__(self, database:Path):
custom_file = database / 'CustomLCs.XML'
default_file = database / 'DefaultLCs.XML'
self.all = {}
try:
self._read_def_liq_class_txt_file(default_file)
except OSError as err:
logging.info("Reading Default LCs: {0} found. Will generate a new one.".format(err))
try:
self._read_def_liq_class_xml_file(default_file)
except OSError as err:
logging.info("OS error: {0}".format(err))
try:
self._read_cus_liq_class_txt_file(custom_file)
except OSError as err:
logging.info("No: {0} found. Will generate a new one.".format(err))
try:
self._read_cus_liq_class_xml_file(custom_file)
except OSError as err:
logging.info("OS error: {0}".format(err))
def _read_cus_liq_class_xml_file(self, custom_file):
tree = ET.parse(custom_file)
root = tree.getroot()
with open(custom_file.with_suffix('.txt'), 'w', encoding='Latin-1', newline='\r\n') as custom:
for lc in root.findall('LiquidClass'):
name = lc.get('name')
liquid_name = lc.get('liquidName')
logging.debug("name='" + name + "' :liquid Name='" + liquid_name + "'")
custom.write(name + "\t" + liquid_name + "\t" + "\n")
lc = LiquidClassDefault(name, liquid_name)
self.all[lc.name] = lc
# logging.debug("to txt- name='" + lc.name + " :liquid Name='" + lc.liquid_name + "'")
def _read_cus_liq_class_txt_file(self, custom_file):
with open(custom_file.with_suffix('.txt'), 'r', encoding='Latin-1') as custom:
logging.info('Parsing: ' + str(custom_file.with_suffix('.txt')))
for lc in custom:
# logging.debug("Line: " + lc)
lc = lc.split('\t')
lc = LiquidClassDefault(name=lc[0], liquid_name=lc[1])
self.all[lc.name] = lc
# logging.debug("from txt- name='" + lc.name + " :liquid Name='" + lc.liquid_name + "'")
def _read_def_liq_class_xml_file(self, default_file):
logging.info("Going to parse LCs from " + str(default_file))
tree = ET.parse(default_file)
root = tree.getroot()
with open(default_file.with_suffix('.txt'), 'w', encoding='Latin-1', newline='\r\n') as default:
logging.info('Writing: ' + str(default_file.with_suffix('.txt').relative_to(base_dir)))
for lc in root.findall('LiquidClass'):
name = lc.get('name')
liquid_name = lc.get('liquidName')
logging.debug("name='" + name + "' :liquid Name='" + liquid_name + "'")
default.write(name + "\t" + liquid_name + "\t" + "\n")
lc = LiquidClassDefault(name, liquid_name)
self.all[lc.name] = lc
# logging.debug("to- name='" + lc.name + " :liquid Name='" + lc.liquid_name + "'")
def _read_def_liq_class_txt_file(self, default_file):
with open(default_file.with_suffix('.txt'), 'r', encoding='Latin-1') as default:
logging.info('Parsing: ' + str(default_file.with_suffix('.txt').relative_to(base_dir)))
for lc in default:
lc = lc.split('\t')
lc = LiquidClassDefault(name=lc[0], liquid_name=lc[1])
self.all[lc.name] = lc
# logging.debug("from txt- name='" + lc.name + " :liquid Name='" + lc.liquid_name + "'")
[docs]class WorkTable:
"""
Collection of carriers.types and Labware.types and pos of instances
"""
cur_worktable = None
[docs] def __init__(self, template_file, robot_protocol=None, grids=67, sites=127):
self.labware_series = {} #: typeName: Series. For each type - a series of labwares (with self have locations)
self.reagents = {} #: connect each reagent name with the reagent self
self.carriers_grid = []
self.carriers_no_grid = [] # in reality in grid with index > real number of grids?
self.template = []
self.n_sites = sites
self.grids = [None] * grids # TODO take this from the template file
self.template_file_name = None
self.def_WashWaste = None
self.def_WashCleaner = None
self.def_DiTiWaste = None
self.def_DiTi_type = None
WorkTable.cur_worktable = self
if isinstance(template_file, list):
self.template = template_file
logging.debug("Template file is a list.")
else:
logging.debug("Set template file: " + str(template_file.relative_to(base_dir)))
self.template = self.parse_worktable_file(template_file, robot_protocol)
self.template_file_name = template_file
[docs] class Location:
"""
One location in a WorkTable
"""
[docs] def __init__(self, grid=None, site=None, carrier=None, carrier_site=None, worktable=None):
"""
:param grid: int, 1-67. worktable grid. Carrier grid position
:param site: int, 0 - 127. Site on carrier (on RAck?) = labware location - (site on carrier - 1) !!!!!
:param carrier:
:param carrier_site:
"""
# A Location have sense only in a WorkTable
self.worktable = worktable or WorkTable.cur_worktable
assert isinstance(self.worktable, WorkTable)
self.carrier = carrier
if grid is None:
assert isinstance(carrier, Carrier)
grid = carrier.grid
assert 1 <= grid <= len(self.worktable.grids)
self.grid = grid
if self.carrier is None:
self.carrier = self.worktable.carriers_grid[self.grid]
site -= 1 # TODO revise - it will be an error if site is None
assert 0 <= site <= self.worktable.n_sites
self.site = site
self.carrier_site = carrier_site # TODO revise
if not self.carrier_site:
self.carrier_site = self.site # TODO revise
if carrier is not None:
assert isinstance(carrier, Carrier)
assert 0 <= self.carrier_site < carrier.type.n_sites, ( "There is no site " + str(site) + " carrier type "
+ carrier.type.name + 'with number of sites '
+ str(carrier.type.n_sites) )
def __str__(self):
return "grid:{grid:d}, site:{site:d}".format(grid=self.grid, site=self.site + 1)
[docs] class File:
[docs] def __init__(self, input, output, worktable):
self.worktable = worktable
self.output = output
self.input = input
self.check_summa : str = None
self.date_time : str = None
self.user : str = None
[docs] def write(self, worktable):
lines = [
self.check_summa,
self.date_time + " " + self.user,
" "*128,
self.roll, # + " "*115,
"--{ RES }--",
"V;" + self.file.version,
"--{ CFG }--",
";".join("999", self.file.n1, self.file.n2, self.file.n3),
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
]
for line in lines:
self.output.write(line)
[docs] def grid_carrier_line(self):
line = "14;"
labw = {}
[docs] def parse_worktable_file(self, template_file, robot_protocol):
if not template_file:
return [] # RETURN
template_list = [] # a grid-line first list the types
with open(template_file, 'r', encoding='Latin-1') as tmpl:
# parsing_grid=False
self.file = WorkTable.File(tmpl, None, self)
template_list += self._read_worktable_header(tmpl)
template_list += self._read_worktable_carriers_grid(tmpl, robot_protocol)
template_list += self._read_worktable_labwares_grid(tmpl, robot_protocol)
for line in tmpl:
template_list += [line]
if line.startswith("--{ RPG }--"): # end of the worktable description
break # BREAK
line = line.split(';')
if line[0] != "998":
continue # CONTINUE
self.template = template_list
self.template_file_name = template_file
return template_list
def _read_worktable_header(self, template):
logging.debug("Read header.")
template_list = [] # a grid-line first list the types
line = template.readline()
template_list += [line]
self.file.check_summa = line
line = template.readline()
template_list += [line]
self.file.date_time, self.file.user = line.split()
line = template.readline()
template_list += [line]
line = template.readline()
template_list += [line]
self.file.roll = line
line = template.readline()
template_list += [line]
assert line.startswith("--{ RES }--")
line = template.readline()
template_list += [line]
v, self.file.version = line.split(';')
assert v == "V"
line = template.readline()
template_list += [line]
assert line.startswith("--{ CFG }--")
line = template.readline()
template_list += [line]
l999, self.file.n1, self.file.n2, self.file.n3 = line.split(';')
assert l999 == "999"
return template_list
def _read_worktable_carriers_grid(self, template, robot_protocol):
template_list = [] # a grid-line first list the types
line = template.readline()
template_list += [line]
carriers = line.split(';')
assert carriers[0] == "14"
self.carriers_grid = []
for idx in carriers[1:-1]:
if idx == "-1":
self.carriers_grid += [None]
continue
idx = int(idx)
self.carriers_grid += [idx] # <--------- set carriers_grid += [idx]
if idx not in robot_protocol.carrier_types().by_index:
logging.warning("Unknow carrier index " + str(idx)
+ " in grid " + str(len(self.carriers_grid) - 1))
else:
logging.info("Carrier: " + robot_protocol.carrier_types().by_index[idx].name
+ " found in line carrier-grid for grid " + str(len(self.carriers_grid) - 1))
logging.info("Detected " + str(len(self.carriers_grid)) + " grids.")
return template_list
def _read_worktable_labwares_grid(self, template, robot_protocol):
logging.debug("Reading grids ")
grid_num = -1
template_list = []
labware_types = [] # set to an empty, False value
for line in template:
template_list += [line]
# logging.debug("Reading grid " + line)
line = line.split(';')
assert line[0] == "998"
if not labware_types: # continue to scan fro carrier grid line with labwares types first
grid_num += 1
# logging.debug("Reading grid " + str(grid_num) + " of " + str(len(self.carriers_grid)))
# logging.debug("1-with " + str(labware_types) + " labwares")
if grid_num >= len(self.carriers_grid): # len(self.grids):
if line[1] != "2":
logging.warning("Non ended 2 Grid " + str(grid_num) \
+ " line " + str(len(template_list)) \
+ ": " + template_list[-1])
return template_list
labware_types = line[2:-1]
assert int(line[1]) == len(labware_types), "Grid " + str(grid_num) \
+ " line " + str(len(template_list)) \
+ ": " + template_list[-1]
else: # we have read the types first, now we need to read the labwares labels
carrier_type_idx = self.carriers_grid[grid_num]
carrier_type = robot_protocol.carrier_types().by_index[carrier_type_idx]
assert len(labware_types) == carrier_type.n_sites
carrier = Carrier(carrier_type=carrier_type, grid=grid_num, worktable=self)
self.carriers_grid[grid_num] = carrier
for site, (labw_type_name, labw_label) in enumerate(zip(labware_types, line[1:-1])):
if not labw_type_name:
if labw_label:
logging.warning("Going to ignore entry - The worktable template have a labware label '" +
labw_label + "' in grid, site: " + str(grid_num) + ", " + str(site) +
" but no labware type")
continue # CONTINUE:
# if labw_type_name and not labw_label:
# logging.warning("WARNING!! Going to ignore entry - The worktable template have a labware type '" +
# labw_type_name + "' in grid, site: " + str(grid_num) + ", " + str(site) +
# " but no labware")
# continue # CONTINUE:
loc = WorkTable.Location(carrier=carrier, site=site + 1, worktable=self)
labw = Labware.create(labw_type_name, loc, labw_label)
if labw:
pass # self.add_labware(labw)
else:
logging.warning("The worktable template have a labware labeled '" +
labw_label + "' in grid, site: " + str(grid_num) + ", " + str(site) +
" but there is no registered labware type '" + labw_type_name + "'")
labware_types = []
[docs] def add_new_labware(self, labware, loc: Location = None):
"""
This will be the first location of this labware. Don't remove from possible old location.
:param labware:
:param loc:
:return:
:raise "This WT have only " + len(self.grid) + " grid.":
"""
assert isinstance(labware, Labware)
if isinstance(loc, WorkTable.Location): # loc take priority
labware.location = loc
assert isinstance(labware.location, WorkTable.Location)
if not isinstance(labware.location.worktable, WorkTable):
labware.location.worktable = self
else:
assert labware.location.worktable is self
if labware.location.grid >= len(self.grids):
raise "This WorkTable have only " + str(len(self.grids)) + " grids. Not " + str(loc.grid)
if labware.location.carrier:
assert labware.location.grid == labware.location.carrier.grid
assert labware.location.carrier is self.carriers_grid[labware.location.grid]
else:
labware.location.carrier = self.carriers_grid[labware.location.grid]
labware.location.carrier.add_labware(labware, labware.location.carrier_site)
for type_name, labw_series in self.labware_series.items(): # loop lab_types already in worktable
for labw in labw_series.labwares: # loop labwares in that series
if labw is labware: # already there ??
logging.warning("The worktable template already have this labware. " +
labw.label + "' in grid, site: " + str(loc.grid) + ", " + str(loc.site + 1))
return
if labware.location.grid == labw.location.grid and \
labware.location.site == labw.location.site:
logging.warning("Trying to add a labware. The worktable template already have a labware with label '"
+ labw.label + "' in grid, site: " + str(loc.grid) + ", " + str(loc.site + 1))
if labware.type.name not in self.labware_series: # first time this type of labware is in this worktable
self.labware_series[labware.type.name] = labware.type.create_series(labware)
else:
self.labware_series[labware.type.name].add(labware)
# todo add to self.grid labware.location.grid dict site, labware
[docs] def add_labware(self, labware, loc : Location):
"""
:param labware:
:param loc:
:return:
:raise "This WT have only " + len(self.grid) + " grid.":
"""
assert isinstance(labware, Labware)
assert isinstance(loc, WorkTable.Location)
if isinstance(labware.location, WorkTable.Location):
if isinstance(labware.location.worktable, WorkTable):
if isinstance(loc.worktable, WorkTable):
if labware.location.worktable is loc.worktable:
labware.location = loc # the simplest intention: move it
return
else:
labware.location.worktable.retire_labware(labware) # remove from previous worktable
return
else: # no new worktable
loc.worktable = labware.location.worktable # just move it in current worktable
labware.location = loc
return
# assert labware.series is None, "For now we assume labware with no location or worktable have no series"
self.retire_labware(labware) # remove from previous worktable/series
loc.worktable.add_new_labware(labware, loc) # add to the new worktable
[docs] def get_current_labware(self, labware):
if isinstance(labware, Labware):
series = labware.series
else:
if isinstance(labware, Labware.Type):
labware = labware.name
if labware not in self.labware_series: # todo what if this is the label of the labware
raise Exception("Labware '" + labware + "' was not found in worktable: " + self.template_file_name)
series = self.labware_series[labware]
return series.current
[docs] def set_current(self, labware):
assert isinstance(labware, Labware)
series = self.labware_series[labware.type.name]
assert isinstance(series, Labware.Type.Series)
series.current = labware
[docs] def get_labware(self, label: (str, int) = None, labw_type=None):
"""
Return a `Labware` already created manually or after the worktable template was scanned.
The labware type is optional (if you provide a label), but it makes the search more robust.
It is mandatory if you provide no label or an index (no label will return the labware with index `0` in the
series of labware of the desired type). The type may be a label or a predefined `Labware.Type`
:type labw_type: (str, Labware.Type)
:param label:
:return Labware
"""
labware = None
if labw_type is None:
assert isinstance(label, str), "Please give at least the labware type or label"
for series in self.labware_series.values():
if label in series.labels:
assert labware is None, ("ERROR: two labware have the label '" + label
+ "' : please indicate the labware type to disambiguate")
labware = series.labels[label]
assert isinstance(labware, Labware)
assert labware is not None, ("ERROR: no labware with the label '" + label
+ "' was found in worktable: " + str(self.template_file_name))
return labware
if isinstance(labw_type, Labware.Type):
labw_type = labw_type.name
assert isinstance(labw_type, str)
series = self.labware_series[labw_type]
if isinstance(label, str):
if label in series.labels:
return series.labels[label]
raise Exception("ERROR: no labware '" + labw_type + "' with the label '" + label
+ "' was found in worktable: " + str(self.template_file_name))
if label is None:
label = 0
assert isinstance(label, int), "ERROR: wrong label type"
return series.labwares[label]
[docs] def set_first_pos(self, labw_type_name=None, posstr=None):
"""
Default to DITI if no labw_type_name is given. chooses a labware by label and set next well or tip to be used.
:param labw_type_name:
:param posstr:
:return:
"""
if labw_type_name:
labw = self.get_current_labware(labw_type_name)
assert labw, "Failed to find first position."
else:
labw = self.get_DITI_series().current
assert labw, "Failed to find first DITI position."
if posstr is None:
posstr = ""
pos = posstr.split('-')
if len(pos) == 2: # assume posstr = 'N-w
labw = labw.series[int(pos[0])-1] # were N is the number of the labw in the series
fpos = pos[1] # and w is the desired position on that labw
else:
# labw = labws[0] # todo use the current. ? or the first ??
fpos = pos[0]
return labw, fpos
[docs] def retire_labware(self, labw):
assert isinstance(labw, Labware )
if labw.type.name in self.labware_series:
assert labw.series is self.labware_series[labw.type.name]
if isinstance(labw.series, Labware.Type.Series):
labw.series.remove(labw)
carrier = None
if labw.location.carrier:
carrier = labw.location.carrier
else:
carrier = self.carriers_grid[labw.location.grid]
assert labw is carrier.labwares[labw.location.carrier_site]
carrier.labwares[labw.location.carrier_site] = None
labw.location = None
return labw
[docs] def replace_with_new(self, labw, label):
assert isinstance(labw, Labware )
loc = labw.location
self.retire_labware(labw)
return labw.type.create_labware(loc, label)
[docs] def set_def_DiTi(self, tips): # :Labware.DITIrackType) ->Labware.DITIrackType:
old = self.def_DiTi_type
self.def_DiTi_type = tips
return old
[docs] def get_DITI_series(self, rack =None):
"""
:type rack:(str, DITIrackType, DITIrack, DITIrackTypeSeries)
"""
if isinstance(rack, DITIrackTypeSeries): # get the series directly
return rack
if isinstance(rack, DITIrack):
return rack.series
if rack is None:
rack = self.def_DiTi_type.name
if isinstance(rack, DITIrackType):
rack = rack.name
assert isinstance(rack, str)
if rack in self.labware_series:
return self.labware_series[rack]
logging.warning("No labware type registered with label: " + rack)
[docs]class Frezeer (WorkTable):
[docs] def __init__(self):
pass
# todo logging.error("******** Implement Frezer ***** ")
# WorkTable.__init__(self)
stock = Frezeer()
[docs]class Carrier:
"""
Collection of Labwares sites, filled with labwares...
"""
[docs] class Types:
[docs] def __init__(self, carrier_file: Path):
self.by_name = {}
self.by_index = {}
self.parse_file(carrier_file)
[docs] def parse_file(self, carrier_file=None):
if not carrier_file:
return [] # RETURN
with open(carrier_file, 'r', encoding='Latin-1') as config:
logging.info("Parsing carriers types from " + str(carrier_file.relative_to(base_dir)))
for line in config:
if line.startswith("13;"): # new Carrier
# logging.debug("line-" + line)
line = line.split(';')
name = line[1]
idx, u = line[2].split("/")
sites = line[-3]
self.add_type(Carrier.Type(name, idx=int(idx), n_sites=int(sites)))
[docs] def add_type(self, carrier_type):
assert isinstance(carrier_type, Carrier.Type)
assert carrier_type.name not in self.by_name, "Duplicate Carrier name: " + carrier_type.name
assert carrier_type.idx not in self.by_index, "Duplicate Carrier index: " + str(carrier_type.idx)
self.by_name[carrier_type.name] = carrier_type
self.by_index[carrier_type.idx] = carrier_type
[docs] class Type:
[docs] def __init__(self, name, idx: int = None, widht_in_grids: int = None, n_sites: int = None):
self.idx = idx
self.widht_in_grids = widht_in_grids
self.n_sites = n_sites
self.allowed_labwares_types = []
self.name = name
def __str__(self):
return self.name
def __repr__(self):
return ((self.name or '-') + '[' + (str(self.idx) or '-') + ']')
[docs] def __init__(self,
carrier_type : Type,
grid : int,
label : str = None,
worktable : WorkTable = None):
worktable = worktable or WorkTable.cur_worktable
self.grid = grid
self.type = carrier_type
self.labwares = [None] * self.type.n_sites
self.label = label
[docs] def add_labware(self, labware, site):
if labware.type.name not in self.type.allowed_labwares_types:
#logging.warning("The labware '" + labware.type.name + ":" + labware.label + "' is not allowed in carrier '"
# + self.type.name + ":" + str(self.label))
logging.info(' self.allow_labware("' + self.type.name + '", "' + labware.type.name + '")')
if site >= self.type.n_sites:
raise "This carrier " + self.type.name + ":" + self.label \
+ " have only " + str(self.type.n_sites) + " sites."
if self.labwares[site] is not None:
logging.warning("Warning: you replaced the labware '" + self.labwares[site].type.name + ":"
+ self.labwares[site].label)
self.labwares[site] = labware
if labware.location.grid != self.grid: # ?????????
if labware.location.grid is not None:
logging.warning("Original grid changed to that of the Rack.")
labware.location.grid = self.grid
def __str__(self):
return (self.type.name or '-') + ': ' + (self.label or '-')
def __repr__(self):
return str(self)
[docs]class Well:
[docs] def __init__(self, labware, Well_Offset):
self.labware = labware
assert isinstance(Well_Offset, int)
self.offset = Well_Offset
self.selFlag = False
self.label = ""
self._vol = 0.0
self._reagent = None
self._actions = []
# self.track = None # todo use this to check what the tips uses?
self.vol = 0.0
self.reagent = None
self.actions = [] # todo transfer actualize this ? how this works?
def __str__(self):
return "well {pos:d}[{p:s}] in labware {lab:s}: {label:s} with {vol:.1f} uL of reagent {what:s}"\
.format(pos =self.offset+1,
p = str(self.labware.position(self.offset)),
lab =self.labware.label,
label=self.label,
vol =self.vol,
what = str(self.reagent or ''))
[docs] class Action:
[docs] def __init__(self, volume:float, origin=None):
self.origin = origin
self.volume = volume
def __str__(self):
strr = "aspirate " if self.volume < 0 else "dispense "
strr += str(self.volume) + " µL of " + str(self.origin)
return strr
[docs] def log(self, vol, origin=None):
self.actions += [Well.Action(vol, origin if origin else self)]
[docs] def select(self, sel=True):
self.selFlag = sel
@property
def vol(self):
return self._vol
@vol.setter
def vol(self, newvol):
self._vol = newvol
@property
def reagent(self):
return self._reagent
@reagent.setter
def reagent(self, reagent):
self._reagent = reagent
@property
def actions(self):
return self._actions
@actions.setter
def actions(self, actions):
self._actions = actions
[docs]class conectedWell(Well):
@property
def vol(self):
return self.labware.vol
@vol.setter
def vol(self, newvol):
self.labware.vol = newvol
@property
def reagent(self):
return self.labware.reagent
@reagent.setter
def reagent(self, reagent):
self.labware.reagent = reagent
@property
def actions(self):
return self.labware.actions
@actions.setter
def actions(self, actions):
self.labware.actions = actions
banned_well = object() # Well(None, 0)
[docs]def count_tips(TIP_MASK : int) -> int:
n = 0
while TIP_MASK:
n += (TIP_MASK & 1)
TIP_MASK = TIP_MASK >> 1
return n
[docs]class Tip: # OK play with this idea
[docs] def __init__(self, rack_type):
assert isinstance(rack_type, DITIrackType) or rack_type is Fixed_Tip
self.vol = 0
self.type = rack_type
def __str__(self):
return "tip {type:s} with {vol:.1f} uL".format(type=self.type.name, vol=self.vol)
[docs]class usedTip(Tip):
[docs] def __init__(self, tip : Tip, origin=None):
Tip.__init__(self, tip.type)
self.vol = tip.vol
self.origin = origin
def __str__(self):
return " Used " + Tip.__str__(self)+" of {what:s}".format(what=str(self.origin))
[docs]class Labware:
# typeName label-string from template worktable file: labwares class-name.
# Mountain a list of labwares types
# like: {'Trough 100ml': <class 'EvoScriPy.Labware.Labware.CuvetteType'>}
types = {}
[docs] class Type:
[docs] class Series:
[docs] def __init__(self, labware): # labware: Labware
assert isinstance(labware, Labware)
self.labwares = []
self.labels = {}
self.type = labware.type
self.add(labware)
self.current = labware
logging.info("Created " + str(self))
def __str__(self):
return "serie of {n:d} {type:s}".format(n=len(self.labwares), type=self.type.name)
def __repr__(self):
return str(self.nCol) or '-'
[docs] def add(self, labware): # labware : Labware
assert self.type is labware.type
self.labwares.append(labware)
self.labels[ labware.label ] = labware
labware.series = self
[docs] def remove(self, labware):
assert isinstance(labware, Labware)
assert self is labware.series
if labware is self.current:
self.set_next()
assert self.current is not labware
del self.labwares[labware]
del self.labels[ labware.label ]
labware.series = None
def __iadd__(self, labware): # labware : Labware
self.add(labware)
[docs] def set_next(self): # -> (Labware, bool): labware: Labware
"""
Set current to the next of self.current
:rtype: (Labware, bool) = (the next labware , serie's current has rotated to the first
:param labware:
"""
self.current, rotated = self.show_next()
return self.current, rotated
[docs] @staticmethod
def set_current_next_to(labware): # -> (Labware, bool): labware: Labware
assert isinstance(labware, Labware)
labware.series.current = labware
return labware.series.set_next()
[docs] @staticmethod
def show_next_to(labware): # -> (Labware, bool): labware: Labware
assert isinstance(labware, Labware)
return labware.series.show_next(labware)
[docs] def show_next(self, labware = None): # -> (Labware, bool): labware: Labware
"""
return next to self.current
:rtype: (Labware, bool) = (the next labware , serie's current has rotated to the first
:param labware:
"""
if labware is None:
labware = self.current
logging.debug("Showing next to " + labware.label)
assert self is labware.series
idx = self.labwares.index(labware) + 1
if idx == len(self.labwares):
return self.labwares[0], True
else:
return self.labwares[idx], False
def __len__(self):
return len(self.labwares)
[docs] def __init__(self, name, nRow, nCol=1, max_vol=None):
assert name not in Labware.types, "Duplicate labware type name: " + name
self.name = name
self.nRow = nRow
self.nCol = nCol
self.max_vol = max_vol
Labware.types[name] = self
def __str__(self):
return "{type:s}".format(type=self.type.name)
def __repr__(self):
return str(self.nCol) or '-'
[docs] def size(self) -> int:
return self.nRow * self.nCol
[docs] def create_labware(self, loc, label):
labw = Labware(self, label, loc)
return labw
[docs] def create_series(self, labware):
return Labware.Type.Series(labware)
[docs] class Position:
[docs] def __init__(self, row, col=1):
self.row = row
self.col = col
[docs] def to_name(self):
return str("-ABCDEFGHIJKLMNOP")[self.row] + '{:02d}'.format(self.col)
def __str__(self):
return self.to_name()
[docs] def __init__(self,
type : Type,
label : str,
location: WorkTable.Location = None):
"""
:param type:
:param label:
:param location:
"""
worktable = None
if isinstance(location, WorkTable.Location): # location take priority
worktable = location.worktable
if not isinstance(worktable, WorkTable):
worktable = WorkTable.cur_worktable
if isinstance(worktable, WorkTable):
location.worktable = worktable # avoid wt.add_labware "moving" new labware
self.location = location
self.type = type
self.label = label
self.series = None
self.Wells = []
if isinstance(worktable, WorkTable):
worktable.add_new_labware(self, location)
self.init_wells()
logging.info("Created labware " + str(self) + " in " + str(self.location))
def __str__(self):
return "{type:s}:{label:s}".format(type=self.type.name, label=self.label)
def __repr__(self):
return ((self.label or '-') + '[' + (str(self.type.nRow) or '-')
+ (str(self.type.nCol) or '-') + ']')
[docs] @staticmethod
def create(labw_t_name : str,
loc : WorkTable.Location,
label : str):
labw_t = Labware.types.get(labw_t_name)
if not labw_t:
logging.warning("There is not labware type defined with label '" + labw_t_name + "'. ")
return None
assert isinstance(labw_t, Labware.Type)
labw = labw_t.create_labware(loc, label)
return labw
[docs] def init_wells(self):
self.Wells = [Well(self, offset) for offset in range(self.type.size())]
[docs] def autoselect(self, offset=0, maxTips=1, replys=1): # OK make this "virtual". Implement cuvette
"""
:param offset:
:param maxTips:
:param replys:
:return:
"""
nWells = self.type.size()
maxTips = min(replys, maxTips)
assert nWells >= offset + maxTips, "Can not select to far" # todo better msg
self.selectOnly(range(offset, offset + maxTips))
return maxTips
[docs] def offset(self, row_pos, col=1):
assert row_pos is not None
if isinstance(row_pos, Well):
assert row_pos.labware is self, "This is a well from another labware."
return row_pos.offset
if isinstance(row_pos, str):
assert col == 1, "Please, define the column only in the row string."
return self.offsetFromName(row_pos)
if isinstance(row_pos, Labware.Position):
col = row_pos.col
row_pos = row_pos.row
if isinstance(row_pos, int):
return row_pos - 1 + (col - 1) * self.type.nRow
assert len(row_pos.replicas) == 1, "Failed to assume a Reagent with only one replica (aliquot)"
return self.offset(row_pos, col)
# assert False, "Unknow row type"
[docs] def offsetFromName(self, wellName):
row = ord(wellName[0]) - ord('A') + 1
col = int(wellName[1:])
return self.offset(row, col)
[docs] def position(self, offset):
return self.Position((offset % self.type.nRow) + 1, (offset // self.type.nRow) + 1)
[docs] def find_free_wells(self, n=1, init_pos=0) -> (bool, [Well]):
continuous = True
free_wells = []
for i in range(init_pos, len(self.Wells) - n+1):
if any(w.reagent for w in self.Wells[i:i + n]):
continue
return continuous, self.Wells[i:i + n]
for w in self.Wells[init_pos:]:
if w.reagent:
continue
free_wells += [w]
if len(free_wells) == n:
break
continuous = all((free_wells[i].offset+1 == free_wells[i+1].offset)
for i in range(len(free_wells)-1))
return continuous, free_wells
[docs] def put(self, reagent, pos=None, num_of_aliquots=None) -> list:
"""
Put a reagent with replicas in the given wells positions of this labware,
and return a list of the wells used
:param reagent:
:param pos: [wells]; if int or [int] will be assumed 1-based not 0-based
:param num_of_aliquots: number of replicas
:return:
"""
num_of_aliquots = num_of_aliquots or reagent.min_num_aliq
if pos is None: # find self where to put the num_of_aliquots of this reagent
continuous, pos = self.find_free_wells(num_of_aliquots)
if len(pos) < num_of_aliquots:
raise NoFreeWells(labware=self,
error=' to put ' + str(num_of_aliquots) + ' aliquots of reagent - ' + str(reagent))
assert num_of_aliquots == len(pos), 'putting reagent - ' + str(reagent) + ' - into Labware: ' \
+ str(self) + ' different replica number (' + str(num_of_aliquots) \
+ ') and number of positions = ' \
+ str(pos) # num_of_aliquots = len(pos) # todo What to do?
elif isinstance(pos, list): # put one replica on each of the given wells position
if num_of_aliquots <= len(pos):
pass # num_of_aliquots = len(pos)
else:
assert (num_of_aliquots == len(pos)), self.label + ": Can not put " + ' aliquots of reagent ' + str(reagent) \
+ " in " + str(len(pos)) + " positions"
elif isinstance(pos, Well): # put one replica beginning from the given position
# assert pos.labware is self, "Trying to put the reagent in another labware?"
pos = pos.labware.Wells[pos.offset: pos.offset + num_of_aliquots]
# pos = self.Wells[pos.offset: pos.offset + num_of_aliquots]
else:
pos = self.offset(pos) # todo: revise !!!!!!!!!!!!!!
pos = self.Wells[pos: pos + num_of_aliquots]
# pos = self.offset(pos) + 1
# pos = range(pos, pos + num_of_aliquots)
aliquots = [] # a list of labware-wells, where the replicas for this reagent are.
for w in pos:
if num_of_aliquots == 0:
return aliquots
w = w if isinstance(w, Well) else self.Wells[self.offset(w)]
assert not w.reagent, self.label + ": Can not put " + reagent.name + " in position " + str(
w.offset + 1) + " already occupied by " + w.reagent.name
w.reagent = reagent
# w.labware = self
aliquots += [w]
num_of_aliquots -= 1
if not len(aliquots):
pass
assert len(aliquots) > 0, 'There are no more free wells in Labware ' + str(self) \
+ ' to put aliquots of reagent - ' + str(reagent)
return aliquots
[docs] def clearSelection(self):
for well in self.Wells:
well.selFlag = False
return self
[docs] def selected(self) -> list :
"""
:return: list of the selected well offset
"""
return [well.offset for well in self.Wells if well.selFlag]
[docs] def selected_wells(self):
return [well for well in self.Wells if well.selFlag]
[docs] def selectAll(self):
for well in self.Wells:
well.selFlag = True
return self
[docs] def selectOnly(self, sel_idx_list):
self.clearSelection()
self.select(sel_idx_list)
return self
[docs] def select(self, sel_idx_list):
if isinstance(sel_idx_list, int):
sel_idx_list = [sel_idx_list]
for w in sel_idx_list:
if not isinstance(w, Well):
w = self.Wells[w]
else:
assert w.labware is self
w.selFlag = True
return self
[docs] def newOffset(self, pos, offset):
return self.offset(pos.row, pos.col) + offset
[docs] def newPosition(self, pos, offset):
return self.position(self.newOffset(pos, offset))
[docs] def posAtParallelMove(self, step, n_tips):
nR, nC = self.type.nRow, self.type.nCol
assert step < nC * nR, "too many steps!!"
SubPlateSize = n_tips * nC
SubPlate = step // SubPlateSize
tN_semiCol = step // n_tips
parit = (SubPlate) % 2
pos_semiCol = nC * parit + (tN_semiCol % nC) * (-1) ** parit + 1 - parit
p = self.Position(row=SubPlate * n_tips + step % n_tips + 1, col=pos_semiCol)
msg = "error in calculation of parallel row {:d}>{:d}".format(p.row, nR)
assert 0 < p.row <= nR, msg
msg = "error in calculation of parallel col {:d}>{:d}".format(p.col, nC)
assert 0 < p.col <= nC, msg
return p
[docs] def parallelOrder(self, n_tips, original=None):
original = original or self.selected()
assert original
if isinstance(original, int):
assert 0 < original <= len(self.Wells)
original = range(original)
assert isinstance(original, (list, range))
return [self.offset(self.posAtParallelMove(offset, n_tips)) for offset in original]
[docs] def offsetAtParallelMove(self, step, n_tips):
p = self.posAtParallelMove(step, n_tips)
return self.offset(p.row, p.col)
[docs] def moveParallel(self, pos, offset): #
return offset % self.type.nCol + 1, offset // self.type.nCol + 1
[docs] def wellSelectionStr(self, wells : (int, [int], [Well] ) = None):
"""
:return: See A.15.3, pag. A-122
file:///C:/Prog/RobotEvo/FreedomEVOwareStandardV2.4SP1-2011.ExtendedDeviceSupportManual.pdf
Many of the advanced worklist commands have a parameter called wellSelection.
wellSelection is a string which specifies the wells (tips) which should be used for
the command.
Characters 1 and 2 of the string specify the number of wells in the x-direction in
hexadecimal. Characters 3 and 4 of the the string specify the number of wells in
the y-direction in hexadecimal. For example, 12 x 8 (96 wells) = 0C08.
All following characters are used for the well selection, whereby each character
specifies the well selection for a group of 7 adjacent wells using a specially
adapted bitmap system. Only 7 bits are used per byte [RANGE 0-127 !!!] instead of 8 to avoid screen
and printer font compatibility problems. Using the 7-bit system, 14 characters are
needed to represent the well selection for 96 wells (plus characters 1 to 4, total of
18 characters) and 55 characters are needed to represent the well selection for
384 wells (total of 59 characters).
In addition, since most ANSI characters below ANSI 32 are non-printable (nonhuman-
readable), decimal 48 (ANSI value for “0”) is added to the value
[RANGE 48-175 !!! 144 have undefined Unicode !!!] of the
bitmap to make it easier to read, send by eMail etc. The following shows some
examples for character 5 of the well selection string for a 96-well microplate in
landcape orientation.
Character 5 is responsible for the first group of 7 wells
this function stores 7 bit per character in the selection string
the first 2 characters are the number of wells in x direction (columns) in hexadecimal.
the characters 3 and 4 are the number of wells in y direction (rows) in hexadecimal.
well are computed in the order back to front, left to right;
https://docs.python.org/3.4/library/string.html#formatstrings
"""
X = self.type.nCol
Y = self.type.nRow
sel = bytearray() # sel=sel.encode('ascii')
bitMask = 0
null = 48 # ord('0')
bit = 0
pre_selected = True
if wells is not None:
if not isinstance(wells, list):
wells = [wells]
if wells is not None:
pre_selected = False
wells = [self.offset(w) for w in wells]
for w in self.Wells:
bit = w.offset % 7
if pre_selected:
if w.selFlag:
bitMask |= (1 << bit)
elif w.offset in wells:
bitMask |= (1 << bit)
if bit == 6:
sel.append(null + bitMask)
bitMask = 0
if bit != 6:
sel.append(null + bitMask)
from EvoScriPy.evo_mode import encoding
return "{:02X}{:02X}".format(X, Y) + sel.decode(encoding)
[docs]class NoFreeWells (Exception):
[docs] def __init__(self, labware: Labware, error: str):
self.labware = labware
Exception.__init__(self, "In " + str(labware) + error)
[docs]class DITIrackTypeSeries(Labware.Type.Series):
[docs] def __init__(self, labware: Labware ):
Labware.Type.Series.__init__(self, labware)
self.last_preserved_tips = None # a tip Well in a DiTi rack
[docs] def find_new_tips(self, TIP_MASK) -> (bool, [Tip]):
"""
:param TIP_MASK:
:param lastPos:
:return:
"""
number_tips = count_tips(TIP_MASK)
tips = []
continuous = True
first = self.current
rack = first
assert isinstance(first, DITIrack)
while (True):
fcontinuous, ftips = rack.find_new_tips(number_tips-len(tips))
tips += ftips
if not fcontinuous:
continuous = False
if number_tips == len(tips):
return continuous, tips
continuous = False
# we need to find in other rack
rack, rotated = self.show_next(rack)
assert rack is not self.current # todo return incomplete ??
[docs] def retire_new_tips(self, TIP_MASK):
"""
A response to a get_tips: the tips have to be removed from the rack
and only after that can appear mounted in the robot arm to pipette.
The tips are removed at the "current" position, the position where
begin the fresh tips, with is maintained internally by the robot and
is unknown to the user
"""
number_tips = count_tips(TIP_MASK) # todo do we really need a correspondence mask - wells??
return self._retire_new_tips(number_tips)
def _retire_new_tips(self, number_tips):
"""
Return removed tips.
Low Level !!! To be called only by implementations of low level Instruction.actualize_robot_state
as response to getDITI2
"""
tips = []
first = self.current
assert isinstance(first, DITIrack)
rack = first
while (True):
tips += rack.retire_new_tips(number_tips-len(tips))
if number_tips == len(tips):
return tips
# we need to find in other rack
rack, rotated = self.set_next()
if rack is first:
logging.warning("Using DITI rack agains? Put new ?")
rack = self.refill_next_rack()
[docs] def refill_next_rack(self, worktable=None): # -> DITIrack
# rack = self.next_rack(worktable) # todo what worktable? another Place ?
next_rack, rotated = self.set_next()
logging.warning("USER PROMPT: ReFill Rack " + next_rack.label) # todo ? USER PROMPT: Fill Rack
assert isinstance(next_rack, DITIrack)
assert self is not next_rack # todo ??? rack empty ??
next_rack.fill() # todo check for tips back !!!!!!
return next_rack
[docs]class DITIrackType(Labware.Type):
[docs] def __init__(self, name, nRow=8, nCol=12, max_vol=None, portrait=False):
if portrait:
nCol, nRow = nRow, nCol # todo revise !
Labware.Type.__init__(self, name, nRow, nCol, max_vol)
self.preserved_tips = {} # order:well ??? sample order:tip well ??sample offset:tip well todo in series?
# self.last_preserved_tips = None # a tip Well in a DiTi rack
[docs] def create_labware(self, loc, label):
labw = DITIrack(self, loc, label)
return labw
[docs] def create_series(self, labware : Labware):
# assert isinstance(labware.type, DITIrackType)
return DITIrackTypeSeries(labware)
[docs]class ProtocolLogicPippetingError(Exception):
pass
[docs]class DITIrack (Labware):
"""
Objects of this class represent physical objects (with location) of
some type Labware.DITIrackType
"""
[docs] def __init__(self, type : DITIrackType,
location: WorkTable.Location,
label : str):
"""
:param type:
:param location:
:param label:
:param worktable:
"""
assert isinstance(type, DITIrackType)
Labware.__init__(self, type, label=label, location = location)
self.pick_next = 0
self.pick_next_back = type.nRow * type.nCol - 1
self.lastPos = False
self.fill()
[docs] def set_DITI_counter(self, posInRack, lastPos = False):
if lastPos:
self.pick_next_back = self.offset(posInRack)
self.lastPos = True
else:
self.pick_next = self.offset(posInRack)
self.lastPos = False
# type.last_preserved_tips = ?
[docs] def fill(self, beg=1, end=None): # todo it belong to Robot ??
if isinstance(beg, list):
assert end is None
end = end if end else self.type.size()
beg = self.offset(beg)
end = self.offset(end)
r = range(beg, end+1)
for w in self.Wells:
w.reagent = None
for w in r:
self.Wells[w].reagent = Tip(self.type) # How we can actualize the "counters"? Using Instructions
# self.Wells[w].labware = self # hummm ??
self.pick_next = beg
self.pick_next_back = end
[docs] def find_new_tips(self, number_tips) -> (bool, [Tip]) :
"""
Return existing tips. May be only partially.
Just to know there are tips
"""
tips = []
pos = self.pick_next_back if self.lastPos else self.pick_next
continuous = True
# begin end direction
r = self.Wells[self.pick_next : self.pick_next_back + 1 : -1 if self.lastPos else 1]
for w in r:
if number_tips == len(tips):
break
pos += -1 if self.lastPos else 1
tip = w.reagent
if isinstance(tip, Tip) and not isinstance(tip, usedTip):
assert tip.type is self.type, "A tip of unexpected type encountered" # todo really??
tips.append(tip)
logging.info("Pick tip " + str(pos + 1) + " from rack site " + str(self.location.site + 1)
+ " named: " + self.label
+ " have " + str(len(tips) )
+ " but need " + str(number_tips))
else:
continuous = False
return continuous, tips
[docs] def retire_new_tips(self, number_tips) -> [Tip]:
"""
Return removed tips. May be only partially.
Low Level !!! To be called only by implementations of low level Instruction.actualize_robot_state
as part of series.retire_new_tips as response to getDITI2
"""
tips = []
pos = 0
# begin end direction
r = self.Wells[self.pick_next : self.pick_next_back + 1 : -1 if self.lastPos else 1]
for w in r:
if number_tips == len(tips):
break
if self.lastPos:
pos = self.pick_next_back
self.pick_next_back -= 1
else:
pos = self.pick_next
self.pick_next += 1
tip = w.reagent
if isinstance(tip, Tip) and not isinstance(tip, usedTip):
assert tip.type is self.type, "A tip of unexpected type encountered" # todo really??
tips.append(tip)
w.reagent = None
logging.info("Pick tip " + str(pos + 1) + " from rack site " + str(self.location.site + 1)
+ " named: " + self.label
+ " have " + str(len(tips) )
+ " but need " + str(number_tips))
return tips
[docs] def set_back(self, TIP_MASK, tips):
""" Low level. Part of the job have been already done: tips is a list of the tips in
the robot arm, passed here just to prevent a call and a link back to the robot.
And the rack self hat already the target tip-wells selected.
:param TIP_MASK:
:param labware_selection:
:param tips:
"""
n = count_tips(TIP_MASK)
assert isinstance(self.type, DITIrackType)
if n != len(self.selected()):
ProtocolLogicPippetingError("Too much or too few wells selected to put tip back")
for i, w in enumerate(self.selected_wells()):
tp = tips[i]
logging.info("Set back " + str(tp) + " in " + str(w) + " of " + self.label)
if w.reagent is Tip:
ProtocolLogicPippetingError("Another tip " + w.reagent.type.name + " is already in position "
+ str(self.position(i)) + " of " + self.label)
assert isinstance(tp, usedTip)
w.reagent = tp
self.type.preserved_tips[tp.origin] = w
self.series.last_preserved_tips = w
[docs] def pick_up(self, TIP_MASK) -> [usedTip]:
""" Low level. Part of the job have been already done: the rack self hat
already the source tip-wells selected. We need to return these tips.
:param TIP_MASK:
"""
n = count_tips(TIP_MASK)
assert n == len(self.selected()), "Too much or too few wells selected to pick up tips"
tips = []
for w in self.selected_wells():
# todo really ? and what if we want to pick up unused tips?
logging.info("Pick Up from " + str(w) + " on the DITI rack " + self.label)
assert isinstance(w.reagent, usedTip), ("No tip " + w.reagent.type.name + " were found in position: "
+ str(w) + " on the DITI rack " + self.label)
tips += [w.reagent]
w.reagent = None
# self.type.preserved_tips[tp.origin.offset] = w # tp.origin.offset
return tips
[docs]class DITIwasteType(Labware.Type):
[docs] def __init__(self, name, capacity=5*96):
Labware.Type.__init__(self, name, nRow=capacity)
[docs] def create_labware(self, loc, label):
labw = DITIwaste(self, loc, label)
return labw
[docs]class DITIwaste(Labware):
[docs] def __init__(self, type, location, label=None):
assert isinstance(type, DITIwasteType)
Labware.__init__(self, type, label, location)
self.wasted = 0
[docs] def waste(self, tips):
for tp in tips:
self.Wells[self.wasted].reagent = tp # todo revise ?
self.wasted += 1 # todo make following assert a Warning or a UserPrompt
assert self.wasted < self.type.size(), "Too much tips wasted. Empty yours DiTi waste."
if isinstance(tp, usedTip): # this tip is dropped and cannot be used any more
react_well = tp.origin
if react_well in tp.type.preserved_tips:
tip_well = tp.type.preserved_tips[react_well]
assert isinstance(tip_well, Well)
if tip_well.reagent is None:
tip_well.reagent = banned_well # don't used this well again (is "contaminated")
del tp.type.preserved_tips[react_well] # todo could be mounted in another position?
else:
assert tp is not tip_well.reagent
[docs]class CuvetteType(Labware.Type):
[docs] def __init__(self, name,
nRow,
max_vol,
nCol=1):
Labware.Type.__init__(self, name, nRow=nRow, max_vol=max_vol, nCol=nCol)
[docs] def create_labware(self, loc, label):
labw = Cuvette(self, loc, label)
return labw
[docs]class Cuvette(Labware):
[docs] def __init__(self, type, location, label=None):
assert isinstance(type, CuvetteType)
self.vol = 0.0
self.reagent = None
self.actions = []
Labware.__init__(self, type, label, location)
[docs] def init_wells(self):
self.Wells = [conectedWell(self, offset) for offset in range(self.type.size())]
[docs] def autoselect(self, offset=0, maxTips=1, replys=1):
"""
:param offset:
:param maxTips:
:param replys:
:return:
"""
nWells = self.type.size()
assert nWells > offset, "Can not select to far" # todo better msg
maxTips = min(maxTips, nWells)
self.selectOnly(range((nWells - maxTips) // 2, (nWells - maxTips) // 2 + maxTips))
return maxTips
[docs]class Te_Mag (Labware.Type):
pass
# "predefining" common labwares types:
Trough_100ml = CuvetteType("Trough 100ml", 8, max_vol= 100000)
Trough_25ml_rec = CuvetteType("Trough 25ml Max. Recovery", 8, max_vol= 25000)
Trough_300ml_MCA= CuvetteType("Trough 300ml MCA", 8, nCol=12, max_vol= 300000) # \todo test it works OK
EppRack16_2mL = Labware.Type("Tube Eppendorf 2mL 16 Pos", 16, max_vol= 2000)
GreinRack16_2mL = Labware.Type("Tube Greinerconic 2mL 16 Pos", 16, max_vol= 2000)
EppRack3x16R = Labware.Type("Tube Eppendorf 3x 16 PosR", 16, 3, max_vol= 1500)
EppRack6x4 = Labware.Type("24 Pos Eppi Tube Rack", 4, 6, max_vol= 1500)
EppRack3x16 = Labware.Type("Tube Eppendorf 3x 16 Pos", 16, 3, max_vol= 1500)
EppRack6x16 = Labware.Type("Tube Eppendorf 6x 16 Pos", 16, 6, max_vol= 1500) # defined in Evoware !!!
MatrixRack1m8x12= Labware.Type("96 Well Matrix Rack 1ml", 8,12, max_vol= 1000)
# by duplicating and then editing the labware 'Tube Eppendorf 3x 16 Pos' and also the carrier
# to have 6 instead of 3 columns. It was needed to change the position X of the last well (96)
# It was done by change the grig 6 positions to the right and coping the X pos of the first well,
# then come back to the originaL GRID AND SET THE COPIED X FOR the last well. The new, duplicated
# carrier was set to X width 150 mm
EppCarr16sites = Carrier.Type("Tube Eppendorf 16 Sites", widht_in_grids=1, n_sites=16)
Greiner2mLx1 = Labware.Type("Tube Greiner conic 2mL 1 Pos", 1, 1, max_vol= 2000)
Epp2mLx1 = Labware.Type("Tube Eppendorf 2mL 1 Pos", 1, 1, max_vol= 2000)
Eppx1 = Labware.Type("Tube Eppendorf 1 Pos", 1, 1, max_vol= 1500)
TubeRack13mmx16 = Labware.Type("Tube 13*100mm 16 Pos", 16, max_vol= 15000) # 15 mL ?
EppRackx16 = Labware.Type("Tube Eppendorf 16 Pos", 16, max_vol= 1500)
EppRack6x16_2mL = Labware.Type("Tube Eppendorf 2m 6x 16 Pos", 16, 6, max_vol= 2000)# todo define in Evoware !!!
DiTi_1000ul = DITIrackType("DiTi 1000ul", max_vol= 940) # 940 ??
DiTi_1000ul_SBS = DITIrackType("DiTi 1000ul SBS LiHa", max_vol= 940) # 940 ??
DiTi_200ul_SBS = DITIrackType("DiTi 200ul SBS LiHa", max_vol= 200) # 190 ??
DiTi_10ul_SBS = DITIrackType("DiTi 10ul SBS LiHa", max_vol= 10) # 0 9,5 ??
DiTi_200ul_MCA96= DITIrackType("DiTi 200ul SBS MCA96", max_vol= 200) # 190 ?? \todo derived ?
DiTi_0200ul = DITIrackType("DiTi 200 ul", max_vol= 190) # ??
Fixed_Tip = Labware.Type("fixed tips", 1, max_vol=1500) # todo ??
Tip_1000maxVol = DiTi_1000ul.max_vol
Tip_200maxVol = 190 # TODO revise
# Evo75_FLI
CleanerShallow = CuvetteType("Wash Station Cleaner shallow" , 8, max_vol= 100000)
WasteWash = CuvetteType("Wash Station Waste", 8, max_vol=10000000) # 10 L
CleanerDeep = CuvetteType("Wash Station Cleaner deep", 8, max_vol= 100000)
DiTi_Waste_plate= DITIwasteType("DiTi Nested Waste MCA384") # Tip Waste ?? Washstation 2Grid DiTi Waste ?? DiTi Nested Waste MCA384 ?? DiTi Waste
# Evo100_FLI
CleanerSWS = CuvetteType("Washstation 2Grid Cleaner short", 8, max_vol= 100000)
WasteWS = CuvetteType("Washstation 2Grid Waste", 8, max_vol=10000000) # 10 L
CleanerLWS = CuvetteType("Washstation 2Grid Cleaner long", 8, max_vol= 100000)
DiTi_Waste = DITIwasteType("Washstation 2Grid DiTi Waste")
TeMag48 = Labware.Type("Tube Eppendorf 48 Pos", 8, 6, max_vol= 1500)
# Evo200_FLI
# CleanerSWS = CuvetteType("Washstation 2Grid Cleaner short", 8, max_vol= 100000) # Cleaner1
# WasteWS = CuvetteType("Washstation 2Grid Waste", 8, max_vol=10000000) # Waste 10 L
# CleanerLWS = CuvetteType("Washstation 2Grid Cleaner long", 8, max_vol= 100000) # Cleaner2
# DiTi_Waste = DITIwasteType("Washstation 2Grid DiTi Waste") # DiTi Waste
AntiCOntamination = CuvetteType("AntiCOntamination", 8, max_vol= 00) # fake, to fix LiHa position
Eppendorfrack = Labware.Type("Sampletubes Eppendorfrack", 16, max_vol= 1500) # = "Tube Eppendorf 16 Pos"
TubeRack15mL2x6 = Labware.Type("Tube Falcon 15ml 12 Pos", 2, 6, max_vol= 15000) # ?? Tube 16 mm 10 Pos
MagSeparatPlt8x9 = Labware.Type("96 Well Separation Plate", 8, 9, max_vol= 00) # Magnetic separation 8x9
Filterplate = Labware.Type("FilterplateaufElutionplate flach", 8, 12, max_vol= 2000) # Te-VacS
MP96MachereyNagel = Labware.Type("96 Well Macherey-Nagel Plate", 8, 12, max_vol= 2000) # Te-VacS
MP96MNflach = Labware.Type("96 Well 8er Macherey-Nagel flach", 8, 12, max_vol= 2000) # Te-VacS
# MP_3Pos = Carrier.Type("MP 3Pos" , width=4, n_sites=3 , idx=12 )
# ContaminationFlyway = Carrier.Type("AntiCOntaminationFlyway" , width=1, n_sites=40, idx=107)
# EppCarr16po = Carrier.Type("Tube Eppendorf 16 Pos" , width=1, n_sites=16, idx=43 )
# Washs2GridTroughDiTi = Carrier.Type("Washstation 2Grid Trough DiTi" , width=2, n_sites=8 , idx=104)
MP96well = Labware.Type("96 Well Microplate" , 8, 12, max_vol= 200)
MP96deepwell = Labware.Type("96 Well DeepWell square", 8, 12, max_vol=2000) # todo define in Evoware !!!
PCR96well = Labware.Type("96 Well PCR Plate" , 8, 12, max_vol= 100)
BioRad96well = Labware.Type("96 Well BioRad" , 8, 12, max_vol= 100)
Box9x9 = Labware.Type("Box 9x9" , 9, 9, max_vol= 2000)
Box10x10 = Labware.Type("Box 10x10" ,10, 10, max_vol= 2000)
[docs]def getLabware(labw_type, label, worktable=None):
worktable = worktable or WorkTable.cur_worktable
return worktable.get_labware(label, labw_type)