mirror of
https://github.com/deepmodeling/Uni-Lab-OS
synced 2026-03-28 11:43:08 +00:00
Add PRCXI functional modules (heating/cooling/shaking/magnetic) and registry config
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
189
unilabos/devices/liquid_handling/prcxi/prcxi_modules.py
Normal file
189
unilabos/devices/liquid_handling/prcxi/prcxi_modules.py
Normal file
@@ -0,0 +1,189 @@
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from .prcxi import PRCXI9300PlateAdapterSite
|
||||
|
||||
|
||||
class PRCXI9300FunctionalModule(PRCXI9300PlateAdapterSite):
|
||||
"""
|
||||
PRCXI 9300 功能模块基类(加热/冷却/震荡/加热震荡/磁吸等)。
|
||||
|
||||
设计目标:
|
||||
- 作为一个可以在工作台上拖拽摆放的实体资源(继承自 ItemizedCarrier)。
|
||||
- 顶面存在一个站点(site),可吸附标准板类资源(plate / tip_rack / tube_rack 等),
|
||||
站点的行为参考 `PRCXI9300PlateAdapterSite` 的实现。
|
||||
- 支持注入 `material_info` (UUID 等),并且在 serialize_state 时做安全过滤,
|
||||
行为与 `PRCXI9300PlateAdapter` 保持一致。
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
size_x: float,
|
||||
size_y: float,
|
||||
size_z: float,
|
||||
module_type: Optional[str] = None,
|
||||
category: str = "module",
|
||||
model: Optional[str] = None,
|
||||
material_info: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
super().__init__(
|
||||
name=name,
|
||||
size_x=size_x,
|
||||
size_y=size_y,
|
||||
size_z=size_z,
|
||||
material_info=material_info,
|
||||
model=model,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# 作为一个“可被槽位吸附”的实体,在 PLR 层面上将其视为 plate_adapter,
|
||||
# 这样与已有的槽位/适配器逻辑兼容;模块语义通过 _unilabos_state 中的
|
||||
# category/module_type 表达。
|
||||
try:
|
||||
self.category = "plate_adapter"
|
||||
except Exception:
|
||||
# category 不是硬性要求属性,失败时静默忽略
|
||||
pass
|
||||
|
||||
# 记录模块类型(加热 / 冷却 / 震荡 / 加热震荡 / 磁吸)
|
||||
# - 通过工厂函数创建时,会显式传入 module_type
|
||||
# - 通过反序列化(deserialize)创建时,可能没有该字段,此时允许为 None
|
||||
self.module_type = module_type or "generic"
|
||||
|
||||
# 与 PRCXI9300PlateAdapter 一致,使用 _unilabos_state 保存扩展信息
|
||||
if not hasattr(self, "_unilabos_state") or self._unilabos_state is None:
|
||||
self._unilabos_state = {}
|
||||
|
||||
# super().__init__ 已经在有 material_info 时写入 "Material",这里仅确保存在
|
||||
if material_info is not None and "Material" not in self._unilabos_state:
|
||||
self._unilabos_state["Material"] = material_info
|
||||
|
||||
# 额外标记 category 和模块类型,便于前端或上层逻辑区分
|
||||
self._unilabos_state.setdefault("category", category)
|
||||
self._unilabos_state["module_type"] = module_type
|
||||
|
||||
def serialize_state(self) -> Dict[str, Dict[str, Any]]:
|
||||
"""
|
||||
在父类基础上,增加对 _unilabos_state 的安全序列化,
|
||||
行为与 `PRCXI9300PlateAdapter.serialize_state` 保持一致。
|
||||
"""
|
||||
try:
|
||||
data = super().serialize_state()
|
||||
except AttributeError:
|
||||
data = {}
|
||||
|
||||
if hasattr(self, "_unilabos_state") and self._unilabos_state:
|
||||
safe_state: Dict[str, Any] = {}
|
||||
for k, v in self._unilabos_state.items():
|
||||
if k == "Material" and isinstance(v, dict):
|
||||
safe_material: Dict[str, Any] = {}
|
||||
for mk, mv in v.items():
|
||||
# 只保留基本数据类型 (字符串, 数字, 布尔值, 列表, 字典, None)
|
||||
if isinstance(mv, (str, int, float, bool, list, dict, type(None))):
|
||||
safe_material[mk] = mv
|
||||
safe_state[k] = safe_material
|
||||
elif isinstance(v, (str, int, float, bool, list, dict, type(None))):
|
||||
safe_state[k] = v
|
||||
|
||||
data.update(safe_state)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 具体功能模块定义
|
||||
# 这里的尺寸和 material_info 目前为占位参数,后续可根据实际测量/JSON 配置进行更新。
|
||||
# 顶面站点尺寸与模块外形一致,保证可以吸附标准 96 板/储液槽等。
|
||||
# ============================================================================
|
||||
|
||||
|
||||
def PRCXI_Heating_Module(name: str) -> PRCXI9300FunctionalModule:
|
||||
"""加热模块(顶面可吸附标准板)。"""
|
||||
return PRCXI9300FunctionalModule(
|
||||
name=name,
|
||||
size_x=127.76,
|
||||
size_y=85.48,
|
||||
size_z=40.0,
|
||||
module_type="heating",
|
||||
model="PRCXI_Heating_Module",
|
||||
material_info={
|
||||
"uuid": "TODO-HEATING-MODULE-UUID",
|
||||
"Code": "HEAT-MOD",
|
||||
"Name": "PRCXI 加热模块",
|
||||
"SupplyType": 3,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def PRCXI_MetalCooling_Module(name: str) -> PRCXI9300FunctionalModule:
|
||||
"""金属冷却模块(顶面可吸附标准板)。"""
|
||||
return PRCXI9300FunctionalModule(
|
||||
name=name,
|
||||
size_x=127.76,
|
||||
size_y=85.48,
|
||||
size_z=40.0,
|
||||
module_type="metal_cooling",
|
||||
model="PRCXI_MetalCooling_Module",
|
||||
material_info={
|
||||
"uuid": "TODO-METAL-COOLING-MODULE-UUID",
|
||||
"Code": "METAL-COOL-MOD",
|
||||
"Name": "PRCXI 金属冷却模块",
|
||||
"SupplyType": 3,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def PRCXI_Shaking_Module(name: str) -> PRCXI9300FunctionalModule:
|
||||
"""震荡模块(顶面可吸附标准板)。"""
|
||||
return PRCXI9300FunctionalModule(
|
||||
name=name,
|
||||
size_x=127.76,
|
||||
size_y=85.48,
|
||||
size_z=50.0,
|
||||
module_type="shaking",
|
||||
model="PRCXI_Shaking_Module",
|
||||
material_info={
|
||||
"uuid": "TODO-SHAKING-MODULE-UUID",
|
||||
"Code": "SHAKE-MOD",
|
||||
"Name": "PRCXI 震荡模块",
|
||||
"SupplyType": 3,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def PRCXI_Heating_Shaking_Module(name: str) -> PRCXI9300FunctionalModule:
|
||||
"""加热震荡模块(顶面可吸附标准板)。"""
|
||||
return PRCXI9300FunctionalModule(
|
||||
name=name,
|
||||
size_x=127.76,
|
||||
size_y=85.48,
|
||||
size_z=55.0,
|
||||
module_type="heating_shaking",
|
||||
model="PRCXI_Heating_Shaking_Module",
|
||||
material_info={
|
||||
"uuid": "TODO-HEATING-SHAKING-MODULE-UUID",
|
||||
"Code": "HEAT-SHAKE-MOD",
|
||||
"Name": "PRCXI 加热震荡模块",
|
||||
"SupplyType": 3,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def PRCXI_Magnetic_Module(name: str) -> PRCXI9300FunctionalModule:
|
||||
"""磁吸模块(顶面可吸附标准板)。"""
|
||||
return PRCXI9300FunctionalModule(
|
||||
name=name,
|
||||
size_x=127.76,
|
||||
size_y=85.48,
|
||||
size_z=30.0,
|
||||
module_type="magnetic",
|
||||
model="PRCXI_Magnetic_Module",
|
||||
material_info={
|
||||
"uuid": "TODO-MAGNETIC-MODULE-UUID",
|
||||
"Code": "MAG-MOD",
|
||||
"Name": "PRCXI 磁吸模块",
|
||||
"SupplyType": 3,
|
||||
},
|
||||
)
|
||||
|
||||
70
unilabos/registry/resources/prcxi/modules.yaml
Normal file
70
unilabos/registry/resources/prcxi/modules.yaml
Normal file
@@ -0,0 +1,70 @@
|
||||
PRCXI_Heating_Module:
|
||||
category:
|
||||
- prcxi
|
||||
- modules
|
||||
class:
|
||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_modules:PRCXI_Heating_Module
|
||||
type: pylabrobot
|
||||
description: '加热模块 (Code: HEAT-MOD)'
|
||||
handles: []
|
||||
icon: ''
|
||||
init_param_schema: {}
|
||||
registry_type: resource
|
||||
version: 1.0.0
|
||||
|
||||
PRCXI_MetalCooling_Module:
|
||||
category:
|
||||
- prcxi
|
||||
- modules
|
||||
class:
|
||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_modules:PRCXI_MetalCooling_Module
|
||||
type: pylabrobot
|
||||
description: '金属冷却模块 (Code: METAL-COOL-MOD)'
|
||||
handles: []
|
||||
icon: ''
|
||||
init_param_schema: {}
|
||||
registry_type: resource
|
||||
version: 1.0.0
|
||||
|
||||
PRCXI_Shaking_Module:
|
||||
category:
|
||||
- prcxi
|
||||
- modules
|
||||
class:
|
||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_modules:PRCXI_Shaking_Module
|
||||
type: pylabrobot
|
||||
description: '震荡模块 (Code: SHAKE-MOD)'
|
||||
handles: []
|
||||
icon: ''
|
||||
init_param_schema: {}
|
||||
registry_type: resource
|
||||
version: 1.0.0
|
||||
|
||||
PRCXI_Heating_Shaking_Module:
|
||||
category:
|
||||
- prcxi
|
||||
- modules
|
||||
class:
|
||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_modules:PRCXI_Heating_Shaking_Module
|
||||
type: pylabrobot
|
||||
description: '加热震荡模块 (Code: HEAT-SHAKE-MOD)'
|
||||
handles: []
|
||||
icon: ''
|
||||
init_param_schema: {}
|
||||
registry_type: resource
|
||||
version: 1.0.0
|
||||
|
||||
PRCXI_Magnetic_Module:
|
||||
category:
|
||||
- prcxi
|
||||
- modules
|
||||
class:
|
||||
module: unilabos.devices.liquid_handling.prcxi.prcxi_modules:PRCXI_Magnetic_Module
|
||||
type: pylabrobot
|
||||
description: '磁吸模块 (Code: MAG-MOD)'
|
||||
handles: []
|
||||
icon: ''
|
||||
init_param_schema: {}
|
||||
registry_type: resource
|
||||
version: 1.0.0
|
||||
|
||||
Reference in New Issue
Block a user