对于PRCXI9320的transfer_group,一对多和多对多

This commit is contained in:
Guangxin Zhang
2025-09-15 00:29:16 +08:00
parent 1cd07915e7
commit 94cdcbf24e
4 changed files with 844 additions and 254 deletions

View File

@@ -16,15 +16,15 @@
"_resource_child_name": "PRCXI_Deck", "_resource_child_name": "PRCXI_Deck",
"_resource_type": "unilabos.devices.liquid_handling.prcxi.prcxi:PRCXI9300Deck" "_resource_type": "unilabos.devices.liquid_handling.prcxi.prcxi:PRCXI9300Deck"
}, },
"host": "10.181.102.13", "host": "172.21.5.75",
"port": 9999, "port": 9999,
"timeout": 10.0, "timeout": 10.0,
"axis": "Right", "axis": "Right",
"channel_num": 1, "channel_num": 1,
"setup": false, "setup": false,
"debug": false, "debug": true,
"simulator": false, "simulator": true,
"matrix_id": "fd383e6d-2d0e-40b5-9c01-1b2870b1f1b1" "matrix_id": "c1d0d5dc-40f2-4f24-97ac-9cc49c68496c"
}, },
"data": {}, "data": {},
"children": [ "children": [

View File

@@ -1,5 +1,5 @@
from __future__ import annotations from __future__ import annotations
import re
import traceback import traceback
from typing import List, Sequence, Optional, Literal, Union, Iterator, Dict, Any, Callable, Set, cast from typing import List, Sequence, Optional, Literal, Union, Iterator, Dict, Any, Callable, Set, cast
from collections import Counter from collections import Counter
@@ -558,14 +558,16 @@ class LiquidHandlerAbstract(LiquidHandlerMiddleware):
# --------------------------------------------------------------- # ---------------------------------------------------------------
def set_group(self, group_name: str, wells: List[Well], volumes: List[float]): def set_group(self, group_name: str, wells: List[Well], volumes: List[float]):
if len(wells) != 8: if self.channel_num == 8 and len(wells) != 8:
raise RuntimeError(f"Expected 8 wells, got {len(wells)}") raise RuntimeError(f"Expected 8 wells, got {len(wells)}")
self.group_info[group_name] = wells self.group_info[group_name] = wells
self.set_liquid(wells, [group_name] * len(wells), volumes) self.set_liquid(wells, [group_name] * len(wells), volumes)
async def transfer_group(self, source_group_name: str, target_group_name: str, unit_volume: float): async def transfer_group(self, source_group_name: str, target_group_name: str, unit_volume: float):
source_wells = self.group_info.get(source_group_name, []) source_wells = self.group_info.get(source_group_name, [])
target_wells = self.group_info.get(target_group_name, []) target_wells = self.group_info.get(target_group_name, [])
rack_info = dict() rack_info = dict()
for child in self.deck.children: for child in self.deck.children:
if issubclass(child.__class__, TipRack): if issubclass(child.__class__, TipRack):
@@ -576,6 +578,7 @@ class LiquidHandlerAbstract(LiquidHandlerMiddleware):
break break
else: else:
rack_info[rack.name] = (rack, tip.maximal_volume - unit_volume) rack_info[rack.name] = (rack, tip.maximal_volume - unit_volume)
if len(rack_info) == 0: if len(rack_info) == 0:
raise ValueError(f"No tip rack can support volume {unit_volume}.") raise ValueError(f"No tip rack can support volume {unit_volume}.")
@@ -583,24 +586,40 @@ class LiquidHandlerAbstract(LiquidHandlerMiddleware):
for child in self.deck.children: for child in self.deck.children:
if child.name == rack_info[0][0]: if child.name == rack_info[0][0]:
target_rack = child target_rack = child
target_rack = cast(TipRack, target_rack) target_rack = cast(TipRack, target_rack)
available_tips = {} available_tips = {}
for (idx, name), tipSpot in zip(target_rack._ordering.items(), target_rack.get_all_items()): for (idx, tipSpot) in enumerate(target_rack.get_all_items()):
if tipSpot.has_tip(): if tipSpot.has_tip():
available_tips[idx] = tipSpot available_tips[idx] = tipSpot
continue continue
# 一般移动液体有两种方式,一对多和多对多
if self.channel_num == 8:
colnum_list = [int(holename[1:]) for holename in available_tips.keys()] tip_prefix = list(available_tips.values())[0].name.split('_')[0]
available_cols = [colnum for colnum, count in dict(Counter(colnum_list)).items() if count == 8] colnum_list = [int(tip.name.split('_')[-1][1:]) for tip in available_tips.values()]
available_cols.sort() # 这样就确定了列号 available_cols = [colnum for colnum, count in dict(Counter(colnum_list)).items() if count == 8]
tips_to_use = [available_tips[f"{chr(65 + i)}{available_cols[0]}"] for i in range(8)] available_cols.sort()
available_tips_dict = {tip.name: tip for tip in available_tips.values()}
tips_to_use = [available_tips_dict[f"{tip_prefix}_{chr(65 + i)}{available_cols[0]}"] for i in range(8)]
await self.pick_up_tips(tips_to_use, use_channels=list(range(0, 8)))
await self.aspirate(source_wells, [unit_volume] * 8, use_channels=list(range(0, 8)))
await self.dispense(target_wells, [unit_volume] * 8, use_channels=list(range(0, 8)))
await self.discard_tips(use_channels=list(range(0, 8)))
await self.pick_up_tips(tips_to_use, use_channels=list(range(0, 8))) elif self.channel_num == 1:
await self.aspirate(source_wells, [10] * 8, use_channels=list(range(0, 8)))
await self.dispense(target_wells, [10] * 8, use_channels=list(range(0, 8))) for num_well in range(len(target_wells)):
await self.discard_tips(use_channels=list(range(0, 8))) tip_to_use = available_tips[list(available_tips.keys())[num_well]]
await self.pick_up_tips([tip_to_use], use_channels=[0])
if len(source_wells) == 1:
await self.aspirate([source_wells[0]], [unit_volume], use_channels=[0])
else:
await self.aspirate([source_wells[num_well]], [unit_volume], use_channels=[0])
await self.dispense([target_wells[num_well]], [unit_volume], use_channels=[0])
await self.discard_tips(use_channels=[0])
else:
raise ValueError(f"Unsupported channel number {self.channel_num}.")
async def create_protocol( async def create_protocol(
self, self,

View File

@@ -0,0 +1,568 @@
import asyncio
import collections
import contextlib
import json
import socket
import time
from typing import Any, List, Dict, Optional, TypedDict, Union, Sequence, Iterator, Literal
from pylabrobot.liquid_handling import (
LiquidHandlerBackend,
Pickup,
SingleChannelAspiration,
Drop,
SingleChannelDispense,
PickupTipRack,
DropTipRack,
MultiHeadAspirationPlate, ChatterBoxBackend, LiquidHandlerChatterboxBackend,
)
from pylabrobot.liquid_handling.standard import (
MultiHeadAspirationContainer,
MultiHeadDispenseContainer,
MultiHeadDispensePlate,
ResourcePickup,
ResourceMove,
ResourceDrop,
)
from pylabrobot.resources import Tip, Deck, Plate, Well, TipRack, Resource, Container, Coordinate, TipSpot, Trash
from unilabos.devices.liquid_handling.liquid_handler_abstract import LiquidHandlerAbstract
class MaterialResource:
"""统一的液体/反应器资源支持多孔wells场景
- wells: 列表每个元素代表一个物料孔unit
- units: 与 wells 对齐的列表,每个元素是 {liquid_id: volume}
- 若传入 liquid_id + volume 或 composition总量将**等分**到各 unit
"""
def __init__(
self,
resource_name: str,
slot: int,
well: List[int],
composition: Optional[Dict[str, float]] = None,
liquid_id: Optional[str] = None,
volume: Union[float, int] = 0.0,
is_supply: Optional[bool] = None,
):
self.resource_name = resource_name
self.slot = int(slot)
self.well = list(well or [])
self.is_supply = bool(is_supply) if is_supply is not None else (bool(composition) or (liquid_id is not None))
# 规范化:至少有 1 个 unit
n = max(1, len(self.well))
self.units: List[Dict[str, float]] = [dict() for _ in range(n)]
# 初始化内容:等分到各 unit
if composition:
for k, v in composition.items():
share = float(v) / n
for u in self.units:
if share > 0:
u[k] = u.get(k, 0.0) + share
elif liquid_id is not None and float(volume) > 0:
share = float(volume) / n
for u in self.units:
u[liquid_id] = u.get(liquid_id, 0.0) + share
# 位置描述
def location(self) -> Dict[str, Any]:
return {"slot": self.slot, "well": self.well}
def unit_count(self) -> int:
return len(self.units)
def unit_volume(self, idx: int) -> float:
return float(sum(self.units[idx].values()))
def total_volume(self) -> float:
return float(sum(self.unit_volume(i) for i in range(self.unit_count())))
def add_to_unit(self, idx: int, liquid_id: str, vol: Union[float, int]):
v = float(vol)
if v < 0:
return
u = self.units[idx]
if liquid_id not in u:
u[liquid_id] = 0.0
if v > 0:
u[liquid_id] += v
def remove_from_unit(self, idx: int, total: Union[float, int]) -> Dict[str, float]:
take = float(total)
if take <= 0: return {}
u = self.units[idx]
avail = sum(u.values())
if avail <= 0: return {}
take = min(take, avail)
ratio = take / avail
removed: Dict[str, float] = {}
for k, v in list(u.items()):
dv = v * ratio
nv = v - dv
if nv < 1e-9: nv = 0.0
u[k] = nv
removed[k] = dv
self.units[idx] = {k: v for k, v in u.items() if v > 0}
return removed
def transfer_unit_to(self, src_idx: int, other: "MaterialResource", dst_idx: int, total: Union[float, int]):
moved = self.remove_from_unit(src_idx, total)
for k, v in moved.items():
other.add_to_unit(dst_idx, k, v)
def get_resource(self) -> Dict[str, Any]:
return {
"resource_name": self.resource_name,
"slot": self.slot,
"well": self.well,
"units": [dict(u) for u in self.units],
"total_volume": self.total_volume(),
"is_supply": self.is_supply,
}
def transfer_liquid(
sources: MaterialResource,
targets: MaterialResource,
unit_volume: Optional[Union[float, int]] = None,
tip: Optional[str] = None, #这里应该是指定种类的
) -> Dict[str, Any]:
try:
vol_each = float(unit_volume)
except (TypeError, ValueError):
return {"action": "transfer_liquid", "error": "invalid unit_volume"}
if vol_each <= 0:
return {"action": "transfer_liquid", "error": "non-positive volume"}
ns, nt = sources.unit_count(), targets.unit_count()
# one-to-many: 从单个 source unit(0) 扇出到目标各 unit
if ns == 1 and nt >= 1:
for j in range(nt):
sources.transfer_unit_to(0, targets, j, vol_each)
# many-to-many: 数量相同,逐一对应
elif ns == nt and ns > 0:
for i in range(ns):
sources.transfer_unit_to(i, targets, i, vol_each)
else:
raise ValueError(f"Unsupported mapping: sources={ns} units, targets={nt} units. Only 1->N or N->N are allowed.")
return {
"action": "transfer_liquid",
"sources": sources.get_resource(),
"targets": targets.get_resource(),
"unit_volume": unit_volume,
"tip": tip,
}
def plan_transfer(pm: "ProtocolManager", **kwargs) -> Dict[str, Any]:
"""Shorthand to add a non-committing transfer to a ProtocolManager.
Accepts the same kwargs as ProtocolManager.add_transfer.
"""
return pm.add_transfer(**kwargs)
class ProtocolManager:
"""Plan/track transfers and backsolve minimum initial volumes.
Use add_transfer(...) to register steps (no mutation).
Use compute_min_initials(...) to infer the minimal starting volume of each liquid
per resource required to execute the plan in order.
"""
# ---------- lifecycle ----------
def __init__(self):
# queued logical steps (keep live refs to MaterialResource)
self.steps: List[Dict[str, Any]] = []
# simple tip catalog; choose the smallest that meets min_aspirate and capacity*safety
self.tip_catalog = [
{"name": "TIP_10uL", "capacity": 10.0, "min_aspirate": 0.5},
{"name": "TIP_20uL", "capacity": 20.0, "min_aspirate": 1.0},
{"name": "TIP_50uL", "capacity": 50.0, "min_aspirate": 2.0},
{"name": "TIP_200uL", "capacity": 200.0, "min_aspirate": 5.0},
{"name": "TIP_300uL", "capacity": 300.0, "min_aspirate": 10.0},
{"name": "TIP_1000uL", "capacity": 1000.0, "min_aspirate": 20.0},
]
# stable labels for unknown liquids per resource (A, B, C, ..., AA, AB, ...)
self._unknown_labels: Dict[MaterialResource, str] = {}
self._unknown_label_counter: int = 0
# ---------- public API ----------
def recommend_tip(self, unit_volume: float, safety: float = 1.10) -> str:
v = float(unit_volume)
# prefer: meets min_aspirate and capacity with safety margin; else fallback to capacity-only; else max capacity
eligible = [t for t in self.tip_catalog if t["min_aspirate"] <= v and t["capacity"] >= v * safety]
if not eligible:
eligible = [t for t in self.tip_catalog if t["capacity"] >= v]
return min(eligible or self.tip_catalog, key=lambda t: t["capacity"]) ["name"]
def get_tip_capacity(self, tip_name: str) -> Optional[float]:
for t in self.tip_catalog:
if t["name"] == tip_name:
return t["capacity"]
return None
def add_transfer(
self,
sources: MaterialResource,
targets: MaterialResource,
unit_volume: Union[float, int],
tip: Optional[str] = None,
) -> Dict[str, Any]:
step = {
"action": "transfer_liquid",
"sources": sources,
"targets": targets,
"unit_volume": float(unit_volume),
"tip": tip or self.recommend_tip(unit_volume),
}
self.steps.append(step)
# return a serializable shadow (no mutation)
return {
"action": "transfer_liquid",
"sources": sources.get_resource(),
"targets": targets.get_resource(),
"unit_volume": step["unit_volume"],
"tip": step["tip"],
}
@staticmethod
def _liquid_keys_of(resource: MaterialResource) -> List[str]:
keys: set[str] = set()
for u in resource.units:
keys.update(u.keys())
return sorted(keys)
@staticmethod
def _fanout_multiplier(ns: int, nt: int) -> Optional[int]:
"""Return the number of liquid movements for a mapping shape.
1->N: N moves; N->N: N moves; otherwise unsupported (None).
"""
if ns == 1 and nt >= 1:
return nt
if ns == nt and ns > 0:
return ns
return None
# ---------- planning core ----------
def compute_min_initials(
self,
use_initial: bool = False,
external_only: bool = True,
) -> Dict[str, Dict[str, float]]:
"""Simulate the plan (nonmutating) and return minimal starting volumes per resource/liquid."""
ledger: Dict[MaterialResource, Dict[str, float]] = {}
min_seen: Dict[MaterialResource, Dict[str, float]] = {}
def _ensure(res: MaterialResource) -> None:
if res in ledger:
return
declared = self._liquid_keys_of(res)
if use_initial:
# sum actual held amounts across units
totals = {k: 0.0 for k in declared}
for u in res.units:
for k, v in u.items():
totals[k] = totals.get(k, 0.0) + float(v)
ledger[res] = totals
else:
ledger[res] = {k: 0.0 for k in declared}
min_seen[res] = {k: ledger[res].get(k, 0.0) for k in ledger[res]}
def _proportions(src: MaterialResource, src_bal: Dict[str, float]) -> tuple[List[str], Dict[str, float]]:
keys = list(src_bal.keys())
total_pos = sum(x for x in src_bal.values() if x > 0)
# if ledger has no keys yet, seed from declared types on the resource
if not keys:
keys = self._liquid_keys_of(src)
for k in keys:
src_bal.setdefault(k, 0.0)
min_seen[src].setdefault(k, 0.0)
if total_pos > 0:
# proportional to current positive balances
props = {k: (src_bal.get(k, 0.0) / total_pos) for k in keys}
return keys, props
# no material currently: evenly from known keys, or assign an unknown label
if keys:
eq = 1.0 / len(keys)
return keys, {k: eq for k in keys}
unk = self._label_for_unknown(src)
keys = [unk]
src_bal.setdefault(unk, 0.0)
min_seen[src].setdefault(unk, 0.0)
return keys, {unk: 1.0}
for step in self.steps:
if step.get("action") != "transfer_liquid":
continue
src: MaterialResource = step["sources"]
dst: MaterialResource = step["targets"]
vol = float(step["unit_volume"])
if vol <= 0:
continue
_ensure(src)
_ensure(dst)
mult = self._fanout_multiplier(src.unit_count(), dst.unit_count())
if not mult:
continue # unsupported mapping shape for this planner
eff_vol = vol * mult
src_bal = ledger[src]
keys, props = _proportions(src, src_bal)
# subtract from src; track minima; accumulate to dst
moved: Dict[str, float] = {}
for k in keys:
dv = eff_vol * props[k]
src_bal[k] = src_bal.get(k, 0.0) - dv
moved[k] = dv
prev_min = min_seen[src].get(k, 0.0)
if src_bal[k] < prev_min:
min_seen[src][k] = src_bal[k]
dst_bal = ledger[dst]
for k, dv in moved.items():
dst_bal[k] = dst_bal.get(k, 0.0) + dv
min_seen[dst].setdefault(k, dst_bal[k])
# convert minima (negative) to required initials
result: Dict[str, Dict[str, float]] = {}
for res, mins in min_seen.items():
if external_only and not getattr(res, "is_supply", False):
continue
need = {liq: max(0.0, -mn) for liq, mn in mins.items() if mn < 0.0}
if need:
result[res.resource_name] = need
return result
def compute_tip_consumption(self) -> Dict[str, Any]:
"""Compute how many tips are consumed at each transfer step, and aggregate by tip type.
Rule: each liquid movement (source unit -> target unit) consumes one tip.
For supported shapes: 1->N uses N tips; N->N uses N tips.
"""
per_step: List[Dict[str, Any]] = []
totals_by_tip: Dict[str, int] = {}
for i, s in enumerate(self.steps):
if s.get("action") != "transfer_liquid":
continue
ns = s["sources"].unit_count()
nt = s["targets"].unit_count()
moves = self._fanout_multiplier(ns, nt) or 0
tip_name = s.get("tip") or self.recommend_tip(s["unit_volume"]) # per-step tip may vary
per_step.append({
"idx": i,
"tip": tip_name,
"tips_used": moves,
"moves": moves,
})
totals_by_tip[tip_name] = totals_by_tip.get(tip_name, 0) + int(moves)
return {"per_step": per_step, "totals_by_tip": totals_by_tip}
def compute_min_initials_with_tips(
self,
use_initial: bool = False,
external_only: bool = True,
) -> Dict[str, Any]:
needs = self.compute_min_initials(use_initial=use_initial, external_only=external_only)
step_tips: List[Dict[str, Any]] = []
totals_by_tip: Dict[str, int] = {}
for i, s in enumerate(self.steps):
if s.get("action") != "transfer_liquid":
continue
ns = s["sources"].unit_count()
nt = s["targets"].unit_count()
moves = self._fanout_multiplier(ns, nt) or 0
tip_name = s.get("tip") or self.recommend_tip(s["unit_volume"]) # step-specific tip
totals_by_tip[self.get_tip_capacity(tip_name)] = totals_by_tip.get(tip_name, 0) + int(moves)
step_tips.append({
"idx": i,
"tip": tip_name,
"tip_capacity": self.get_tip_capacity(tip_name),
"unit_volume": s["unit_volume"],
"tips_used": moves,
})
return {"liquid_setup": needs, "step_tips": step_tips, "totals_by_tip": totals_by_tip}
# ---------- unknown labels ----------
def _index_to_letters(self, idx: int) -> str:
"""0->A, 1->B, ... 25->Z, 26->AA, 27->AB ... (Excel-like)"""
s: List[str] = []
idx = int(idx)
while True:
idx, r = divmod(idx, 26)
s.append(chr(ord('A') + r))
if idx == 0:
break
idx -= 1 # Excel-style carry
return "".join(reversed(s))
def _label_for_unknown(self, res: MaterialResource) -> str:
"""Assign a stable unknown-liquid label (A/B/C/...) per resource."""
if res not in self._unknown_labels:
lab = self._index_to_letters(self._unknown_label_counter)
self._unknown_label_counter += 1
self._unknown_labels[res] = lab
return self._unknown_labels[res]
# 在这一步传输目前有的物料
class LabResource:
def __init__(self):
self.tipracks = []
self.plates = []
self.trash = []
def add_tipracks(self, tiprack: List[TipRack]):
self.tipracks.extend(tiprack)
def add_plates(self, plate: List[Plate]):
self.plates.extend(plate)
def add_trash(self, trash: List[Plate]):
self.trash.extend(trash)
def get_resources_info(self) -> Dict[str, Any]:
tipracks = [{"name": tr.name, "max_volume": tr.children[0].tracker._tip.maximal_volume, "count": len(tr.children)} for tr in self.tipracks]
plates = [{"name": pl.name, "max_volume": pl.children[0].max_volume, "count": len(pl.children)} for pl in self.plates]
trash = [{"name": t.name, "max_volume": t.children[0].max_volume, "count": len(t.children)} for t in self.trash]
return {
"tipracks": tipracks,
"plates": plates,
"trash": trash
}
from typing import Dict, Any
class DefaultLayout:
def __init__(self, product_name: str = "PRCXI9300"):
self.labresource = None
if product_name not in ["PRCXI9300", "PRCXI9320"]:
raise ValueError(f"Unsupported product_name: {product_name}. Only 'PRCXI9300' and 'PRCXI9320' are supported.")
if product_name == "PRCXI9300":
self.rows = 2
self.columns = 3
self.layout = [1, 2, 3, 4, 5, 6]
self.trash_slot = 3
self.waste_liquid_slot = 6
elif product_name == "PRCXI9320":
self.rows = 3
self.columns = 4
self.layout = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
self.trash_slot = 3
self.waste_liquid_slot = 12
def get_layout(self) -> Dict[str, Any]:
return {
"rows": self.rows,
"columns": self.columns,
"layout": self.layout,
"trash_slot": self.trash_slot,
"waste_liquid_slot": self.waste_liquid_slot
}
def get_trash_slot(self) -> int:
return self.trash_slot
def get_waste_liquid_slot(self) -> int:
return self.waste_liquid_slot
def set_liquid_handler_layout(self, product_name: str):
if product_name == "PRCXI9300":
self.rows = 2
self.columns = 3
self.layout = [1, 2, 3, 4, 5, 6]
self.trash_slot = 3
self.waste_liquid_slot = 6
elif product_name == "PRCXI9320":
self.rows = 3
self.columns = 4
self.layout = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
self.trash_slot = 3
self.waste_liquid_slot = 12
def set_trash_slot(self, slot: int):
self.trash_slot = slot
def set_waste_liquid_slot(self, slot: int):
self.waste_liquid_slot = slot
def add_lab_resource(self, lab_resource: LabResource):
self.labresource = lab_resource.get_resources_info()
def recommend_layout(self, needs: Dict[str, Any]) -> Dict[str, Any]:
"""根据 needs 推荐布局"""
liquid_info = needs['liquid_setup']
tip_info = needs['totals_by_tip'] # 修改这里:直接访问 totals_by_tip
print("当前实验所需物料信息:", liquid_info)
print("当前实验所需枪头信息:", tip_info)
print(self.labresource)
for liquid in liquid_info:
# total_volume = liquid.values()
print(liquid)
#print(f"资源 {liquid} 需要的总体积: {total_volume}")
if __name__ == "__main__":
# ---- 资源SUP 供液X中间板 R14 孔空),目标板 R24 孔空)----
sup = MaterialResource("SUP", slot=5, well=[1], liquid_id="X", volume=10000)
r1 = MaterialResource("R1", slot=6, well=[1,2,3,4,5,6,7,8])
r2 = MaterialResource("R2", slot=7, well=[1,2,3,4,5,6,7,8])
pm = ProtocolManager()
# 步骤1SUP -> R11->N 扇出,每孔 50 uL总 200 uL
pm.add_transfer(sup, r1, unit_volume=10.0)
# 步骤2R1 -> R2N->N 对应,每对 25 uL总 100 uL来自 R1 中已存在的混合物 X
pm.add_transfer(r1, r2, unit_volume=120.0)
out = pm.compute_min_initials_with_tips()
# layout_planer = DefaultLayout('PRCXI9320')
# print(layout_planer.get_layout())
# print("回推最小需求:", out["liquid_setup"]) # {'SUP': {'X': 200.0}}
# print("步骤枪头建议:", out["step_tips"]) # [{'idx':0,'tip':'TIP_200uL','unit_volume':50.0}, {'idx':1,'tip':'TIP_50uL','unit_volume':25.0}]
# # 实际执行(可选)
# transfer_liquid(sup, r1, unit_volume=50.0)
# transfer_liquid(r1, r2, unit_volume=25.0)
# print("执行后 SUP", sup.get_resource()) # 总体积 -200
# print("执行后 R1", r1.get_resource()) # 每孔 25 uL50 进 -25 出)
# print("执行后 R2", r2.get_resource()) # 每孔 25 uL
from pylabrobot.resources.opentrons.tube_racks import *
from pylabrobot.resources.opentrons.plates import *
from pylabrobot.resources.opentrons.tip_racks import *
from pylabrobot.resources.opentrons.reservoirs import *
plate = [locals()['nest_96_wellplate_2ml_deep'](name="thermoscientificnunc_96_wellplate_2000ul"), locals()['corning_96_wellplate_360ul_flat'](name="corning_96_wellplate_360ul_flat")]
tiprack = [locals()['opentrons_96_tiprack_300ul'](name="opentrons_96_tiprack_300ul"), locals()['opentrons_96_tiprack_1000ul'](name="opentrons_96_tiprack_1000ul")]
trash = [locals()['axygen_1_reservoir_90ml'](name="axygen_1_reservoir_90ml")]
from pprint import pprint
lab_resource = LabResource()
lab_resource.add_tipracks(tiprack)
lab_resource.add_plates(plate)
lab_resource.add_trash(trash)
layout_planer = DefaultLayout('PRCXI9300')
layout_planer.add_lab_resource(lab_resource)
layout_planer.recommend_layout(out)

View File

@@ -981,121 +981,119 @@ if __name__ == "__main__":
# 4. # 4.
deck = PRCXI9300Deck(name="PRCXI_Deck_9300", size_x=100, size_y=100, size_z=100) # deck = PRCXI9300Deck(name="PRCXI_Deck_9300", size_x=100, size_y=100, size_z=100)
from pylabrobot.resources.opentrons.tip_racks import opentrons_96_tiprack_300ul,opentrons_96_tiprack_10ul # from pylabrobot.resources.opentrons.tip_racks import opentrons_96_tiprack_300ul,opentrons_96_tiprack_10ul
from pylabrobot.resources.opentrons.plates import corning_96_wellplate_360ul_flat, nest_96_wellplate_2ml_deep # from pylabrobot.resources.opentrons.plates import corning_96_wellplate_360ul_flat, nest_96_wellplate_2ml_deep
def get_well_container(name: str) -> PRCXI9300Container: # def get_well_container(name: str) -> PRCXI9300Container:
well_containers = corning_96_wellplate_360ul_flat(name).serialize() # well_containers = corning_96_wellplate_360ul_flat(name).serialize()
plate = PRCXI9300Container(name=name, size_x=50, size_y=50, size_z=10, category="plate", # plate = PRCXI9300Container(name=name, size_x=50, size_y=50, size_z=10, category="plate",
ordering=well_containers["ordering"]) # ordering=well_containers["ordering"])
plate_serialized = plate.serialize() # plate_serialized = plate.serialize()
plate_serialized["parent_name"] = deck.name # plate_serialized["parent_name"] = deck.name
well_containers.update({k: v for k, v in plate_serialized.items() if k not in ["children"]}) # well_containers.update({k: v for k, v in plate_serialized.items() if k not in ["children"]})
new_plate: PRCXI9300Container = PRCXI9300Container.deserialize(well_containers) # new_plate: PRCXI9300Container = PRCXI9300Container.deserialize(well_containers)
return new_plate # return new_plate
def get_tip_rack(name: str) -> PRCXI9300Container: # def get_tip_rack(name: str) -> PRCXI9300Container:
tip_racks = opentrons_96_tiprack_300ul("name").serialize() # tip_racks = opentrons_96_tiprack_300ul("name").serialize()
tip_rack = PRCXI9300Container(name=name, size_x=50, size_y=50, size_z=10, category="tip_rack", # tip_rack = PRCXI9300Container(name=name, size_x=50, size_y=50, size_z=10, category="tip_rack",
ordering=tip_racks["ordering"]) # ordering=tip_racks["ordering"])
tip_rack_serialized = tip_rack.serialize() # tip_rack_serialized = tip_rack.serialize()
tip_rack_serialized["parent_name"] = deck.name # tip_rack_serialized["parent_name"] = deck.name
tip_racks.update({k: v for k, v in tip_rack_serialized.items() if k not in ["children"]}) # tip_racks.update({k: v for k, v in tip_rack_serialized.items() if k not in ["children"]})
new_tip_rack: PRCXI9300Container = PRCXI9300Container.deserialize(tip_racks) # new_tip_rack: PRCXI9300Container = PRCXI9300Container.deserialize(tip_racks)
return new_tip_rack # return new_tip_rack
plate1 = get_tip_rack("RackT1") # plate1 = get_tip_rack("RackT1")
plate1.load_state({ # plate1.load_state({
"Material": { # "Material": {
"uuid": "076250742950465b9d6ea29a225dfb00", # "uuid": "076250742950465b9d6ea29a225dfb00",
"Code": "ZX-001-300", # "Code": "ZX-001-300",
"Name": "300μL Tip头" # "Name": "300μL Tip头"
} # }
}) # })
plate2 = get_well_container("PlateT2") # plate2 = get_well_container("PlateT2")
plate2.load_state({ # plate2.load_state({
"Material": { # "Material": {
"uuid": "57b1e4711e9e4a32b529f3132fc5931f", # "uuid": "57b1e4711e9e4a32b529f3132fc5931f",
"Code": "ZX-019-2.2", # "Code": "ZX-019-2.2",
"Name": "96深孔板" # "Name": "96深孔板"
} # }
}) # })
plate3 = PRCXI9300Trash("trash", size_x=50, size_y=100, size_z=10, category="trash") # plate3 = PRCXI9300Trash("trash", size_x=50, size_y=100, size_z=10, category="trash")
plate3.load_state({ # plate3.load_state({
"Material": { # "Material": {
"uuid": "730067cf07ae43849ddf4034299030e9" # "uuid": "730067cf07ae43849ddf4034299030e9"
} # }
}) # })
plate4 = get_well_container("PlateT4") # plate4 = get_well_container("PlateT4")
plate4.load_state({ # plate4.load_state({
"Material": { # "Material": {
"uuid": "57b1e4711e9e4a32b529f3132fc5931f", # "uuid": "57b1e4711e9e4a32b529f3132fc5931f",
"Code": "ZX-019-2.2", # "Code": "ZX-019-2.2",
"Name": "96深孔板" # "Name": "96深孔板"
} # }
}) # })
plate5 = get_well_container("PlateT5") # plate5 = get_well_container("PlateT5")
plate5.load_state({ # plate5.load_state({
"Material": { # "Material": {
"uuid": "57b1e4711e9e4a32b529f3132fc5931f", # "uuid": "57b1e4711e9e4a32b529f3132fc5931f",
"Code": "ZX-019-2.2", # "Code": "ZX-019-2.2",
"Name": "96深孔板" # "Name": "96深孔板"
} # }
}) # })
plate6 = get_well_container("PlateT6") # plate6 = get_well_container("PlateT6")
plate6.load_state({ # plate6.load_state({
"Material": { # "Material": {
"uuid": "57b1e4711e9e4a32b529f3132fc5931f", # "uuid": "57b1e4711e9e4a32b529f3132fc5931f",
"Code": "ZX-019-2.2", # "Code": "ZX-019-2.2",
"Name": "96深孔板" # "Name": "96深孔板"
} # }
}) # })
deck.assign_child_resource(plate1, location=Coordinate(0, 0, 0)) # deck.assign_child_resource(plate1, location=Coordinate(0, 0, 0))
deck.assign_child_resource(plate2, location=Coordinate(0, 0, 0)) # deck.assign_child_resource(plate2, location=Coordinate(0, 0, 0))
deck.assign_child_resource(plate3, location=Coordinate(0, 0, 0)) # deck.assign_child_resource(plate3, location=Coordinate(0, 0, 0))
deck.assign_child_resource(plate4, location=Coordinate(0, 0, 0)) # deck.assign_child_resource(plate4, location=Coordinate(0, 0, 0))
deck.assign_child_resource(plate5, location=Coordinate(0, 0, 0)) # deck.assign_child_resource(plate5, location=Coordinate(0, 0, 0))
deck.assign_child_resource(plate6, location=Coordinate(0, 0, 0)) # deck.assign_child_resource(plate6, location=Coordinate(0, 0, 0))
# plate_2_liquids = [[('water', 500)]]*96 # # # plate_2_liquids = [[('water', 500)]]*96
# plate2.set_well_liquids(plate_2_liquids) # # # plate2.set_well_liquids(plate_2_liquids)
handler = PRCXI9300Handler(deck=deck, host="10.181.214.132", port=9999, # handler = PRCXI9300Handler(deck=deck, host="10.181.214.132", port=9999,
timeout=10.0, setup=False, debug=False, # timeout=10.0, setup=False, debug=False,
simulator=True, # simulator=True,
matrix_id="71593", # matrix_id="71593",
channel_num=8, axis="Left") # Initialize the handler with the deck and host settings # channel_num=8, axis="Left") # Initialize the handler with the deck and host settings
# plate_2_liquids = handler.set_group("water", plate2.children[:8], [200]*8)
# plate5_liquids = handler.set_group("master_mix", plate5.children[:8], [100]*8)
# handler.set_tiprack([plate1])
# asyncio.run(handler.setup()) # Initialize the handler and setup the connection
plate_2_liquids = handler.set_group("water", plate2.children[:8], [200]*8) # from pylabrobot.resources import set_volume_tracking
plate5_liquids = handler.set_group("master_mix", plate5.children[:8], [100]*8)
handler.set_tiprack([plate1])
asyncio.run(handler.setup()) # Initialize the handler and setup the connection
from pylabrobot.resources import set_volume_tracking
# from pylabrobot.resources import set_tip_tracking # from pylabrobot.resources import set_tip_tracking
set_volume_tracking(enabled=True) # set_volume_tracking(enabled=True)
# from unilabos.resources.graphio import * # from unilabos.resources.graphio import *
# # A = tree_to_list([resource_plr_to_ulab(deck)]) # # A = tree_to_list([resource_plr_to_ulab(deck)])
# # with open("deck_9300_new.json", "w", encoding="utf-8") as f: # # with open("deck_9300_new.json", "w", encoding="utf-8") as f:
# # json.dump(A, f, indent=4, ensure_ascii=False) # # json.dump(A, f, indent=4, ensure_ascii=False)
asyncio.run(handler.create_protocol(protocol_name="Test Protocol")) # Initialize the backend and setup the connection # asyncio.run(handler.create_protocol(protocol_name="Test Protocol")) # Initialize the backend and setup the connection
asyncio.run(handler.transfer_group("water", "master_mix", 100)) # Reset tip tracking # asyncio.run(handler.transfer_group("water", "master_mix", 100)) # Reset tip tracking
# asyncio.run(handler.pick_up_tips(plate1.children[:8],[0,1,2,3,4,5,6,7])) # asyncio.run(handler.pick_up_tips(plate1.children[:8],[0,1,2,3,4,5,6,7]))
# print(plate1.children[:8]) # print(plate1.children[:8])
@@ -1166,164 +1164,172 @@ if __name__ == "__main__":
### 9320 ### ### 9320 ###
# deck = PRCXI9300Deck(name="PRCXI_Deck", size_x=100, size_y=100, size_z=100) deck = PRCXI9300Deck(name="PRCXI_Deck", size_x=100, size_y=100, size_z=100)
# from pylabrobot.resources.opentrons.tip_racks import tipone_96_tiprack_200ul,opentrons_96_tiprack_10ul from pylabrobot.resources.opentrons.tip_racks import tipone_96_tiprack_200ul,opentrons_96_tiprack_10ul
# from pylabrobot.resources.opentrons.plates import corning_96_wellplate_360ul_flat, nest_96_wellplate_2ml_deep from pylabrobot.resources.opentrons.plates import corning_96_wellplate_360ul_flat, nest_96_wellplate_2ml_deep
# def get_well_container(name: str) -> PRCXI9300Container: def get_well_container(name: str) -> PRCXI9300Container:
# well_containers = corning_96_wellplate_360ul_flat(name).serialize() well_containers = corning_96_wellplate_360ul_flat(name).serialize()
# plate = PRCXI9300Container(name=name, size_x=50, size_y=50, size_z=10, category="plate", plate = PRCXI9300Container(name=name, size_x=50, size_y=50, size_z=10, category="plate",
# ordering=collections.OrderedDict()) ordering=collections.OrderedDict())
# plate_serialized = plate.serialize() plate_serialized = plate.serialize()
# plate_serialized["parent_name"] = deck.name plate_serialized["parent_name"] = deck.name
# well_containers.update({k: v for k, v in plate_serialized.items() if k not in ["children"]}) well_containers.update({k: v for k, v in plate_serialized.items() if k not in ["children"]})
# new_plate: PRCXI9300Container = PRCXI9300Container.deserialize(well_containers) new_plate: PRCXI9300Container = PRCXI9300Container.deserialize(well_containers)
# return new_plate return new_plate
# def get_tip_rack(name: str) -> PRCXI9300Container: def get_tip_rack(name: str) -> PRCXI9300Container:
# tip_racks = opentrons_96_tiprack_10ul("name").serialize() tip_racks = opentrons_96_tiprack_10ul("name").serialize()
# tip_rack = PRCXI9300Container(name=name, size_x=50, size_y=50, size_z=10, category="tip_rack", tip_rack = PRCXI9300Container(name=name, size_x=50, size_y=50, size_z=10, category="tip_rack",
# ordering=collections.OrderedDict()) ordering=collections.OrderedDict())
# tip_rack_serialized = tip_rack.serialize() tip_rack_serialized = tip_rack.serialize()
# tip_rack_serialized["parent_name"] = deck.name tip_rack_serialized["parent_name"] = deck.name
# tip_racks.update({k: v for k, v in tip_rack_serialized.items() if k not in ["children"]}) tip_racks.update({k: v for k, v in tip_rack_serialized.items() if k not in ["children"]})
# new_tip_rack: PRCXI9300Container = PRCXI9300Container.deserialize(tip_racks) new_tip_rack: PRCXI9300Container = PRCXI9300Container.deserialize(tip_racks)
# return new_tip_rack return new_tip_rack
# plate1 = get_well_container("HPLCPlateT1") plate1 = get_well_container("HPLCPlateT1")
# plate1.load_state({ plate1.load_state({
# "Material": { "Material": {
# "uuid": "548bbc3df0d4447586f2c19d2c0c0c55", "uuid": "548bbc3df0d4447586f2c19d2c0c0c55",
# "Code": "HPLC01", "Code": "HPLC01",
# "Name": "HPLC料盘" "Name": "HPLC料盘"
# } }
# }) })
# plate2 = get_well_container("PlateT2") plate2 = get_well_container("PlateT2")
# plate2.load_state({ plate2.load_state({
# "Material": { "Material": {
# "uuid": "04211a2dc93547fe9bf6121eac533650", "uuid": "04211a2dc93547fe9bf6121eac533650",
# } }
# }) })
# plate3 = get_well_container("PlateT3") plate3 = get_well_container("PlateT3")
# plate3.load_state({ plate3.load_state({
# "Material": { "Material": {
# "uuid": "04211a2dc93547fe9bf6121eac533650", "uuid": "04211a2dc93547fe9bf6121eac533650",
# } }
# }) })
# trash = PRCXI9300Trash(name="trash", size_x=50, size_y=50, size_z=10, category="trash") trash = PRCXI9300Trash(name="trash", size_x=50, size_y=50, size_z=10, category="trash")
# trash.load_state({ trash.load_state({
# "Material": { "Material": {
# "uuid": "730067cf07ae43849ddf4034299030e9" "uuid": "730067cf07ae43849ddf4034299030e9"
# } }
# }) })
# plate5 = get_well_container("PlateT5") plate5 = get_well_container("PlateT5")
# plate5.load_state({ plate5.load_state({
# "Material": { "Material": {
# "uuid": "04211a2dc93547fe9bf6121eac533650", "uuid": "04211a2dc93547fe9bf6121eac533650",
# } }
# }) })
# plate6 = get_well_container("PlateT6") plate6 = get_well_container("PlateT6")
# plate6.load_state({ plate6.load_state({
# "Material": { "Material": {
# "uuid": "04211a2dc93547fe9bf6121eac533650" "uuid": "04211a2dc93547fe9bf6121eac533650"
# } }
# }) })
# plate7 = PRCXI9300Container(name="plateT7", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()) plate7 = PRCXI9300Container(name="plateT7", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict())
# plate7.load_state({ plate7.load_state({
# "Material": { "Material": {
# "uuid": "04211a2dc93547fe9bf6121eac533650" "uuid": "04211a2dc93547fe9bf6121eac533650"
# } }
# }) })
# plate8 = get_tip_rack("RackT8") plate8 = get_tip_rack("RackT8")
# plate8.load_state({ plate8.load_state({
# "Material": { "Material": {
# "uuid": "068b3815e36b4a72a59bae017011b29f", "uuid": "068b3815e36b4a72a59bae017011b29f",
# "Code": "ZX-001-10+", "Code": "ZX-001-10+",
# "Name": "10μL加长 Tip头" "Name": "10μL加长 Tip头"
# } }
# }) })
# plate9 = get_well_container("PlateT9") plate9 = get_well_container("PlateT9")
# plate9.load_state({ plate9.load_state({
# "Material": { "Material": {
# "uuid": "04211a2dc93547fe9bf6121eac533650" "uuid": "04211a2dc93547fe9bf6121eac533650"
# } }
# }) })
# plate10 = get_well_container("PlateT10") plate10 = get_well_container("PlateT10")
# plate10.load_state({ plate10.load_state({
# "Material": { "Material": {
# "uuid": "04211a2dc93547fe9bf6121eac533650" "uuid": "04211a2dc93547fe9bf6121eac533650"
# } }
# }) })
# plate11 = get_well_container("PlateT11") plate11 = get_well_container("PlateT11")
# plate11.load_state({ plate11.load_state({
# "Material": { "Material": {
# "uuid": "57b1e4711e9e4a32b529f3132fc5931f", "uuid": "57b1e4711e9e4a32b529f3132fc5931f",
# } }
# }) })
# plate12 = get_well_container("PlateT12") plate12 = get_well_container("PlateT12")
# plate12.load_state({ plate12.load_state({
# "Material": { "Material": {
# "uuid": "04211a2dc93547fe9bf6121eac533650" "uuid": "04211a2dc93547fe9bf6121eac533650"
# } }
# }) })
# plate13 = get_well_container("PlateT13") plate13 = get_well_container("PlateT13")
# plate13.load_state({ plate13.load_state({
# "Material": { "Material": {
# "uuid": "04211a2dc93547fe9bf6121eac533650" "uuid": "04211a2dc93547fe9bf6121eac533650"
# } }
# }) })
# # container_for_nothing = PRCXI9300Container(name="container_for_nothing", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()) # container_for_nothing = PRCXI9300Container(name="container_for_nothing", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict())
# deck.assign_child_resource(plate1, location=Coordinate(0, 0, 0)) deck.assign_child_resource(plate1, location=Coordinate(0, 0, 0))
# deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing1", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0)) deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing1", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0))
# deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing2", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0)) deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing2", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0))
# deck.assign_child_resource(trash, location=Coordinate(0, 0, 0)) deck.assign_child_resource(trash, location=Coordinate(0, 0, 0))
# deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing3", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0)) deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing3", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0))
# deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0)) deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0))
# deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing4", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0)) deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing4", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0))
# deck.assign_child_resource(plate8, location=Coordinate(0, 0, 0)) deck.assign_child_resource(plate8, location=Coordinate(0, 0, 0))
# deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing5", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0)) deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing5", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0))
# deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing6", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0)) deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing6", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0))
# deck.assign_child_resource(plate11, location=Coordinate(0, 0, 0)) deck.assign_child_resource(plate11, location=Coordinate(0, 0, 0))
# deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing7", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0)) deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing7", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0))
# deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing8", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0)) deck.assign_child_resource(PRCXI9300Container(name="container_for_nothing8", size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()), location=Coordinate(0, 0, 0))
# handler = PRCXI9300Handler(deck=deck, host="10.181.102.13", port=9999, handler = PRCXI9300Handler(deck=deck, host="172.21.5.75", port=9999,
# timeout=10.0, setup=False, debug=False, timeout=10.0, setup=False, debug=True,
# matrix_id="fd383e6d-2d0e-40b5-9c01-1b2870b1f1b1", matrix_id="c1d0d5dc-40f2-4f24-97ac-9cc49c68496c",
# channel_num=8, axis="Right") # Initialize the handler with the deck and host settings channel_num=1, axis="Left",simulator=True) # Initialize the handler with the deck and host settings
# handler.set_tiprack([plate8]) # Set the tip rack for the handler handler.set_tiprack([plate8]) # Set the tip rack for the handler
# asyncio.run(handler.setup()) # Initialize the handler and setup the connection asyncio.run(handler.setup()) # Initialize the handler and setup the connection
# from pylabrobot.resources import set_volume_tracking from pylabrobot.resources import set_volume_tracking
# # from pylabrobot.resources import set_tip_tracking # from pylabrobot.resources import set_tip_tracking
# set_volume_tracking(enabled=True) set_volume_tracking(enabled=True)
# plate11.set_well_liquids([("Water", 100) if (i % 8 == 0 and i // 8 < 6) else (None, 100) for i in range(96)]) # Set liquids for every 8 wells in plate8 plate_2_liquids = handler.set_group("water", [plate2.children[0]], [300])
#print(plate_2_liquids)
plate5_liquids = handler.set_group("master_mix", plate5.children[:23], [100]*23)
#print(plate5_liquids)
# from unilabos.resources.graphio import * # plate11.set_well_liquids([("Water", 100) if (i % 8 == 0 and i // 8 < 6) else (None, 100) for i in range(96)]) # Set liquids for every 8 wells in plate8
from unilabos.resources.graphio import *
# A = tree_to_list([resource_plr_to_ulab(deck)]) # A = tree_to_list([resource_plr_to_ulab(deck)])
# # with open("deck.json", "w", encoding="utf-8") as f: # # with open("deck.json", "w", encoding="utf-8") as f:
# # json.dump(A, f, indent=4, ensure_ascii=False) # # json.dump(A, f, indent=4, ensure_ascii=False)
# print(plate11.get_well(0).tracker.get_used_volume()) # print(plate11.get_well(0).tracker.get_used_volume())
# asyncio.run(handler.create_protocol(protocol_name="Test Protocol")) # Initialize the backend and setup the connection asyncio.run(handler.create_protocol(protocol_name="Test Protocol")) # Initialize the backend and setup the connection
# # asyncio.run(handler.pick_up_tips([plate8.children[8]],[0])) asyncio.run(handler.transfer_group("water", "master_mix", 10)) # Reset tip tracking
# # print(plate8.children[8])
# # # asyncio.run(handler.run_protocol())
# # asyncio.run(handler.aspirate([plate11.children[0]],[10], [0])) # asyncio.run(handler.pick_up_tips([plate8.children[8]],[0]))
# # print(plate11.children[0]) # print(plate8.children[8])
# # # asyncio.run(handler.run_protocol()) # asyncio.run(handler.run_protocol())
# # asyncio.run(handler.dispense([plate1.children[0]],[10],[0])) # asyncio.run(handler.aspirate([plate11.children[0]],[10], [0]))
# # print(plate1.children[0]) # print(plate11.children[0])
# # # asyncio.run(handler.run_protocol()) # # asyncio.run(handler.run_protocol())
# # asyncio.run(handler.mix([plate1.children[0]], mix_time=3, mix_vol=5, height_to_bottom=0.5, offsets=Coordinate(0, 0, 0), mix_rate=100)) # asyncio.run(handler.dispense([plate1.children[0]],[10],[0]))
# # print(plate1.children[0]) # print(plate1.children[0])
# # asyncio.run(handler.discard_tips()) # # asyncio.run(handler.run_protocol())
# asyncio.run(handler.mix([plate1.children[0]], mix_time=3, mix_vol=5, height_to_bottom=0.5, offsets=Coordinate(0, 0, 0), mix_rate=100))
# print(plate1.children[0])
# asyncio.run(handler.discard_tips([0]))
# asyncio.run(handler.add_liquid( # asyncio.run(handler.add_liquid(
# asp_vols=[10]*7, # asp_vols=[10]*7,
@@ -1341,10 +1347,7 @@ if __name__ == "__main__":
# spread="custom", # spread="custom",
# )) # ))
# asyncio.run(handler.run_protocol()) # Run the protocol # asyncio.run(handler.run_protocol()) # Run the protocol
@@ -1413,7 +1416,7 @@ if __name__ == "__main__":
# # # )) # # # ))
# # print(json.dumps(handler._unilabos_backend.steps_todo_list, indent=2)) # Print matrix info # # print(json.dumps(handler._unilabos_backend.steps_todo_list, indent=2)) # Print matrix info
# # # input("pick_up_tips add step") # # # input("pick_up_tips add step")
# # #asyncio.run(handler.run_protocol()) # Run the protocol #asyncio.run(handler.run_protocol()) # Run the protocol
# # # input("Running protocol...") # # # input("Running protocol...")
# # # input("Press Enter to continue...") # Wait for user input before proceeding # # # input("Press Enter to continue...") # Wait for user input before proceeding
# # # print("PRCXI9300Handler initialized with deck and host settings.") # # # print("PRCXI9300Handler initialized with deck and host settings.")