mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2025-12-17 21:11:12 +00:00
* 修改lh的json启动 * 修改lh的json启动 * 修改backend,做成sim的通用backend * 修改yaml的地址,3D模型适配网页生产环境 * 添加laiyu硬件连接 * 修改移液枪的状态判断方法, 修改移液枪的状态判断方法, 添加三轴的表定点与零点之间的转换 添加三轴真实移动的backend * 修改laiyu移液站 简化移动方法, 取消软件限制位置, 修改当值使用Z轴时也需要重新复位Z轴的问题 * 更新lh以及laiyu workshop 1,现在可以直接通过修改backend,适配其他的移液站,主类依旧使用LiquidHandler,不用重新编写 2,修改枪头判断标准,使用枪头自身判断而不是类的判断, 3,将归零参数用毫米计算,方便手动调整, 4,修改归零方式,上电使用机械归零,确定机械零点,手动归零设置工作区域零点方便计算,二者互不干涉 * 修改枪头动作 * 修改虚拟仿真方法 --------- Co-authored-by: zhangshixiang <@zhangshixiang> Co-authored-by: Junhan Chang <changjh@dp.tech>
334 lines
9.9 KiB
Python
334 lines
9.9 KiB
Python
"""
|
||
LaiYu液体处理设备后端实现
|
||
|
||
提供设备的后端接口和控制逻辑
|
||
"""
|
||
|
||
import logging
|
||
from typing import Dict, Any, Optional, List
|
||
from abc import ABC, abstractmethod
|
||
|
||
# 尝试导入PyLabRobot后端
|
||
try:
|
||
from pylabrobot.liquid_handling.backends import LiquidHandlerBackend
|
||
PYLABROBOT_AVAILABLE = True
|
||
except ImportError:
|
||
PYLABROBOT_AVAILABLE = False
|
||
# 创建模拟后端基类
|
||
class LiquidHandlerBackend:
|
||
def __init__(self, name: str):
|
||
self.name = name
|
||
self.is_connected = False
|
||
|
||
def connect(self):
|
||
"""连接设备"""
|
||
pass
|
||
|
||
def disconnect(self):
|
||
"""断开连接"""
|
||
pass
|
||
|
||
|
||
class LaiYuLiquidBackend(LiquidHandlerBackend):
|
||
"""LaiYu液体处理设备后端"""
|
||
|
||
def __init__(self, name: str = "LaiYu_Liquid_Backend"):
|
||
"""
|
||
初始化LaiYu液体处理设备后端
|
||
|
||
Args:
|
||
name: 后端名称
|
||
"""
|
||
if PYLABROBOT_AVAILABLE:
|
||
# PyLabRobot 的 LiquidHandlerBackend 不接受参数
|
||
super().__init__()
|
||
else:
|
||
# 模拟版本接受 name 参数
|
||
super().__init__(name)
|
||
|
||
self.name = name
|
||
self.logger = logging.getLogger(__name__)
|
||
self.is_connected = False
|
||
self.device_info = {
|
||
"name": "LaiYu液体处理设备",
|
||
"version": "1.0.0",
|
||
"manufacturer": "LaiYu",
|
||
"model": "LaiYu_Liquid_Handler"
|
||
}
|
||
|
||
def connect(self) -> bool:
|
||
"""
|
||
连接到LaiYu液体处理设备
|
||
|
||
Returns:
|
||
bool: 连接是否成功
|
||
"""
|
||
try:
|
||
self.logger.info("正在连接到LaiYu液体处理设备...")
|
||
# 这里应该实现实际的设备连接逻辑
|
||
# 目前返回模拟连接成功
|
||
self.is_connected = True
|
||
self.logger.info("成功连接到LaiYu液体处理设备")
|
||
return True
|
||
except Exception as e:
|
||
self.logger.error(f"连接LaiYu液体处理设备失败: {e}")
|
||
self.is_connected = False
|
||
return False
|
||
|
||
def disconnect(self) -> bool:
|
||
"""
|
||
断开与LaiYu液体处理设备的连接
|
||
|
||
Returns:
|
||
bool: 断开连接是否成功
|
||
"""
|
||
try:
|
||
self.logger.info("正在断开与LaiYu液体处理设备的连接...")
|
||
# 这里应该实现实际的设备断开连接逻辑
|
||
self.is_connected = False
|
||
self.logger.info("成功断开与LaiYu液体处理设备的连接")
|
||
return True
|
||
except Exception as e:
|
||
self.logger.error(f"断开LaiYu液体处理设备连接失败: {e}")
|
||
return False
|
||
|
||
def is_device_connected(self) -> bool:
|
||
"""
|
||
检查设备是否已连接
|
||
|
||
Returns:
|
||
bool: 设备是否已连接
|
||
"""
|
||
return self.is_connected
|
||
|
||
def get_device_info(self) -> Dict[str, Any]:
|
||
"""
|
||
获取设备信息
|
||
|
||
Returns:
|
||
Dict[str, Any]: 设备信息字典
|
||
"""
|
||
return self.device_info.copy()
|
||
|
||
def home_device(self) -> bool:
|
||
"""
|
||
设备归零操作
|
||
|
||
Returns:
|
||
bool: 归零是否成功
|
||
"""
|
||
if not self.is_connected:
|
||
self.logger.error("设备未连接,无法执行归零操作")
|
||
return False
|
||
|
||
try:
|
||
self.logger.info("正在执行设备归零操作...")
|
||
# 这里应该实现实际的设备归零逻辑
|
||
self.logger.info("设备归零操作完成")
|
||
return True
|
||
except Exception as e:
|
||
self.logger.error(f"设备归零操作失败: {e}")
|
||
return False
|
||
|
||
def aspirate(self, volume: float, location: Dict[str, Any]) -> bool:
|
||
"""
|
||
吸液操作
|
||
|
||
Args:
|
||
volume: 吸液体积 (微升)
|
||
location: 吸液位置信息
|
||
|
||
Returns:
|
||
bool: 吸液是否成功
|
||
"""
|
||
if not self.is_connected:
|
||
self.logger.error("设备未连接,无法执行吸液操作")
|
||
return False
|
||
|
||
try:
|
||
self.logger.info(f"正在执行吸液操作: 体积={volume}μL, 位置={location}")
|
||
# 这里应该实现实际的吸液逻辑
|
||
self.logger.info("吸液操作完成")
|
||
return True
|
||
except Exception as e:
|
||
self.logger.error(f"吸液操作失败: {e}")
|
||
return False
|
||
|
||
def dispense(self, volume: float, location: Dict[str, Any]) -> bool:
|
||
"""
|
||
排液操作
|
||
|
||
Args:
|
||
volume: 排液体积 (微升)
|
||
location: 排液位置信息
|
||
|
||
Returns:
|
||
bool: 排液是否成功
|
||
"""
|
||
if not self.is_connected:
|
||
self.logger.error("设备未连接,无法执行排液操作")
|
||
return False
|
||
|
||
try:
|
||
self.logger.info(f"正在执行排液操作: 体积={volume}μL, 位置={location}")
|
||
# 这里应该实现实际的排液逻辑
|
||
self.logger.info("排液操作完成")
|
||
return True
|
||
except Exception as e:
|
||
self.logger.error(f"排液操作失败: {e}")
|
||
return False
|
||
|
||
def pick_up_tip(self, location: Dict[str, Any]) -> bool:
|
||
"""
|
||
取枪头操作
|
||
|
||
Args:
|
||
location: 枪头位置信息
|
||
|
||
Returns:
|
||
bool: 取枪头是否成功
|
||
"""
|
||
if not self.is_connected:
|
||
self.logger.error("设备未连接,无法执行取枪头操作")
|
||
return False
|
||
|
||
try:
|
||
self.logger.info(f"正在执行取枪头操作: 位置={location}")
|
||
# 这里应该实现实际的取枪头逻辑
|
||
self.logger.info("取枪头操作完成")
|
||
return True
|
||
except Exception as e:
|
||
self.logger.error(f"取枪头操作失败: {e}")
|
||
return False
|
||
|
||
def drop_tip(self, location: Dict[str, Any]) -> bool:
|
||
"""
|
||
丢弃枪头操作
|
||
|
||
Args:
|
||
location: 丢弃位置信息
|
||
|
||
Returns:
|
||
bool: 丢弃枪头是否成功
|
||
"""
|
||
if not self.is_connected:
|
||
self.logger.error("设备未连接,无法执行丢弃枪头操作")
|
||
return False
|
||
|
||
try:
|
||
self.logger.info(f"正在执行丢弃枪头操作: 位置={location}")
|
||
# 这里应该实现实际的丢弃枪头逻辑
|
||
self.logger.info("丢弃枪头操作完成")
|
||
return True
|
||
except Exception as e:
|
||
self.logger.error(f"丢弃枪头操作失败: {e}")
|
||
return False
|
||
|
||
def move_to(self, location: Dict[str, Any]) -> bool:
|
||
"""
|
||
移动到指定位置
|
||
|
||
Args:
|
||
location: 目标位置信息
|
||
|
||
Returns:
|
||
bool: 移动是否成功
|
||
"""
|
||
if not self.is_connected:
|
||
self.logger.error("设备未连接,无法执行移动操作")
|
||
return False
|
||
|
||
try:
|
||
self.logger.info(f"正在移动到位置: {location}")
|
||
# 这里应该实现实际的移动逻辑
|
||
self.logger.info("移动操作完成")
|
||
return True
|
||
except Exception as e:
|
||
self.logger.error(f"移动操作失败: {e}")
|
||
return False
|
||
|
||
def get_status(self) -> Dict[str, Any]:
|
||
"""
|
||
获取设备状态
|
||
|
||
Returns:
|
||
Dict[str, Any]: 设备状态信息
|
||
"""
|
||
return {
|
||
"connected": self.is_connected,
|
||
"device_info": self.device_info,
|
||
"status": "ready" if self.is_connected else "disconnected"
|
||
}
|
||
|
||
# PyLabRobot 抽象方法实现
|
||
def stop(self):
|
||
"""停止所有操作"""
|
||
self.logger.info("停止所有操作")
|
||
pass
|
||
|
||
@property
|
||
def num_channels(self) -> int:
|
||
"""返回通道数量"""
|
||
return 1 # 单通道移液器
|
||
|
||
def can_pick_up_tip(self, tip_rack, tip_position) -> bool:
|
||
"""检查是否可以拾取吸头"""
|
||
return True # 简化实现,总是返回True
|
||
|
||
def pick_up_tips(self, tip_rack, tip_positions):
|
||
"""拾取多个吸头"""
|
||
self.logger.info(f"拾取吸头: {tip_positions}")
|
||
pass
|
||
|
||
def drop_tips(self, tip_rack, tip_positions):
|
||
"""丢弃多个吸头"""
|
||
self.logger.info(f"丢弃吸头: {tip_positions}")
|
||
pass
|
||
|
||
def pick_up_tips96(self, tip_rack):
|
||
"""拾取96个吸头"""
|
||
self.logger.info("拾取96个吸头")
|
||
pass
|
||
|
||
def drop_tips96(self, tip_rack):
|
||
"""丢弃96个吸头"""
|
||
self.logger.info("丢弃96个吸头")
|
||
pass
|
||
|
||
def aspirate96(self, volume, plate, well_positions):
|
||
"""96通道吸液"""
|
||
self.logger.info(f"96通道吸液: 体积={volume}")
|
||
pass
|
||
|
||
def dispense96(self, volume, plate, well_positions):
|
||
"""96通道排液"""
|
||
self.logger.info(f"96通道排液: 体积={volume}")
|
||
pass
|
||
|
||
def pick_up_resource(self, resource, location):
|
||
"""拾取资源"""
|
||
self.logger.info(f"拾取资源: {resource}")
|
||
pass
|
||
|
||
def drop_resource(self, resource, location):
|
||
"""放置资源"""
|
||
self.logger.info(f"放置资源: {resource}")
|
||
pass
|
||
|
||
def move_picked_up_resource(self, resource, location):
|
||
"""移动已拾取的资源"""
|
||
self.logger.info(f"移动资源: {resource} 到 {location}")
|
||
pass
|
||
|
||
|
||
def create_laiyu_backend(name: str = "LaiYu_Liquid_Backend") -> LaiYuLiquidBackend:
|
||
"""
|
||
创建LaiYu液体处理设备后端实例
|
||
|
||
Args:
|
||
name: 后端名称
|
||
|
||
Returns:
|
||
LaiYuLiquidBackend: 后端实例
|
||
"""
|
||
return LaiYuLiquidBackend(name) |