mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2025-12-15 13:44:39 +00:00
add plr_to_bioyond, and refactor bioyond stations
This commit is contained in:
@@ -37,7 +37,7 @@ def _initialize_material_system(self, deck_config: Dict[str, Any], children_conf
|
||||
**定义在**: `workstation_base.py`
|
||||
|
||||
**设计目的**:
|
||||
- 提供外部物料系统(如Bioyong、LIMS等)集成的标准接口
|
||||
- 提供外部物料系统(如Bioyond、LIMS等)集成的标准接口
|
||||
- 双向同步:从外部系统同步到本地deck,以及将本地变更同步到外部系统
|
||||
- 处理外部系统的变更通知
|
||||
|
||||
@@ -59,7 +59,7 @@ async def handle_external_change(self, change_info: Dict[str, Any]) -> bool:
|
||||
**扩展功能**:
|
||||
- HTTP报送接收服务集成
|
||||
- 具体工作流实现(液体转移、板洗等)
|
||||
- Bioyong物料系统同步器示例
|
||||
- Bioyond物料系统同步器示例
|
||||
- 外部报送处理方法
|
||||
|
||||
## 技术栈
|
||||
@@ -142,11 +142,11 @@ success = workstation.execute_workflow("liquid_transfer", {
|
||||
### 3. 外部系统集成
|
||||
|
||||
```python
|
||||
class BioyongResourceSynchronizer(ResourceSynchronizer):
|
||||
"""Bioyong系统同步器"""
|
||||
class BioyondResourceSynchronizer(ResourceSynchronizer):
|
||||
"""Bioyond系统同步器"""
|
||||
|
||||
async def sync_from_external(self) -> bool:
|
||||
# 从Bioyong API获取物料
|
||||
# 从Bioyond API获取物料
|
||||
external_materials = await self._fetch_bioyong_materials()
|
||||
|
||||
# 转换并添加到本地deck
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,824 @@
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
from unilabos.devices.workstation.bioyond_studio.bioyond_rpc import BioyondException
|
||||
from unilabos.devices.workstation.bioyond_studio.station import BioyondWorkstation
|
||||
|
||||
|
||||
class BioyondDispensingStation(BioyondWorkstation):
|
||||
def __init__(self, config):
|
||||
super().__init__(config)
|
||||
# self.config = config
|
||||
# self.api_key = config["api_key"]
|
||||
# self.host = config["api_host"]
|
||||
#
|
||||
# # 使用简单的Logger替代原来的logger
|
||||
# self._logger = SimpleLogger()
|
||||
# self.is_running = False
|
||||
|
||||
# 90%10%小瓶投料任务创建方法
|
||||
def create_90_10_vial_feeding_task(self,
|
||||
order_name: str = None,
|
||||
speed: str = None,
|
||||
temperature: str = None,
|
||||
delay_time: str = None,
|
||||
percent_90_1_assign_material_name: str = None,
|
||||
percent_90_1_target_weigh: str = None,
|
||||
percent_90_2_assign_material_name: str = None,
|
||||
percent_90_2_target_weigh: str = None,
|
||||
percent_90_3_assign_material_name: str = None,
|
||||
percent_90_3_target_weigh: str = None,
|
||||
percent_10_1_assign_material_name: str = None,
|
||||
percent_10_1_target_weigh: str = None,
|
||||
percent_10_1_volume: str = None,
|
||||
percent_10_1_liquid_material_name: str = None,
|
||||
percent_10_2_assign_material_name: str = None,
|
||||
percent_10_2_target_weigh: str = None,
|
||||
percent_10_2_volume: str = None,
|
||||
percent_10_2_liquid_material_name: str = None,
|
||||
percent_10_3_assign_material_name: str = None,
|
||||
percent_10_3_target_weigh: str = None,
|
||||
percent_10_3_volume: str = None,
|
||||
percent_10_3_liquid_material_name: str = None,
|
||||
hold_m_name: str = None) -> dict:
|
||||
"""
|
||||
创建90%10%小瓶投料任务
|
||||
|
||||
参数说明:
|
||||
- order_name: 任务名称,如果为None则使用默认名称
|
||||
- speed: 搅拌速度,如果为None则使用默认值400
|
||||
- temperature: 温度,如果为None则使用默认值40
|
||||
- delay_time: 延迟时间,如果为None则使用默认值600
|
||||
- percent_90_1_assign_material_name: 90%_1物料名称
|
||||
- percent_90_1_target_weigh: 90%_1目标重量
|
||||
- percent_90_2_assign_material_name: 90%_2物料名称
|
||||
- percent_90_2_target_weigh: 90%_2目标重量
|
||||
- percent_90_3_assign_material_name: 90%_3物料名称
|
||||
- percent_90_3_target_weigh: 90%_3目标重量
|
||||
- percent_10_1_assign_material_name: 10%_1固体物料名称
|
||||
- percent_10_1_target_weigh: 10%_1固体目标重量
|
||||
- percent_10_1_volume: 10%_1液体体积
|
||||
- percent_10_1_liquid_material_name: 10%_1液体物料名称
|
||||
- percent_10_2_assign_material_name: 10%_2固体物料名称
|
||||
- percent_10_2_target_weigh: 10%_2固体目标重量
|
||||
- percent_10_2_volume: 10%_2液体体积
|
||||
- percent_10_2_liquid_material_name: 10%_2液体物料名称
|
||||
- percent_10_3_assign_material_name: 10%_3固体物料名称
|
||||
- percent_10_3_target_weigh: 10%_3固体目标重量
|
||||
- percent_10_3_volume: 10%_3液体体积
|
||||
- percent_10_3_liquid_material_name: 10%_3液体物料名称
|
||||
- hold_m_name: 库位名称,如"C01",用于查找对应的holdMId
|
||||
|
||||
返回: 任务创建结果
|
||||
|
||||
异常:
|
||||
- BioyondException: 各种错误情况下的统一异常
|
||||
"""
|
||||
try:
|
||||
# 1. 参数验证
|
||||
if not hold_m_name:
|
||||
raise BioyondException("hold_m_name 是必填参数")
|
||||
|
||||
# 检查90%物料参数的完整性
|
||||
# 90%_1物料:如果有物料名称或目标重量,就必须有全部参数
|
||||
if percent_90_1_assign_material_name or percent_90_1_target_weigh:
|
||||
if not percent_90_1_assign_material_name:
|
||||
raise BioyondException("90%_1物料:如果提供了目标重量,必须同时提供物料名称")
|
||||
if not percent_90_1_target_weigh:
|
||||
raise BioyondException("90%_1物料:如果提供了物料名称,必须同时提供目标重量")
|
||||
|
||||
# 90%_2物料:如果有物料名称或目标重量,就必须有全部参数
|
||||
if percent_90_2_assign_material_name or percent_90_2_target_weigh:
|
||||
if not percent_90_2_assign_material_name:
|
||||
raise BioyondException("90%_2物料:如果提供了目标重量,必须同时提供物料名称")
|
||||
if not percent_90_2_target_weigh:
|
||||
raise BioyondException("90%_2物料:如果提供了物料名称,必须同时提供目标重量")
|
||||
|
||||
# 90%_3物料:如果有物料名称或目标重量,就必须有全部参数
|
||||
if percent_90_3_assign_material_name or percent_90_3_target_weigh:
|
||||
if not percent_90_3_assign_material_name:
|
||||
raise BioyondException("90%_3物料:如果提供了目标重量,必须同时提供物料名称")
|
||||
if not percent_90_3_target_weigh:
|
||||
raise BioyondException("90%_3物料:如果提供了物料名称,必须同时提供目标重量")
|
||||
|
||||
# 检查10%物料参数的完整性
|
||||
# 10%_1物料:如果有物料名称、目标重量、体积或液体物料名称中的任何一个,就必须有全部参数
|
||||
if any([percent_10_1_assign_material_name, percent_10_1_target_weigh, percent_10_1_volume, percent_10_1_liquid_material_name]):
|
||||
if not percent_10_1_assign_material_name:
|
||||
raise BioyondException("10%_1物料:如果提供了其他参数,必须同时提供固体物料名称")
|
||||
if not percent_10_1_target_weigh:
|
||||
raise BioyondException("10%_1物料:如果提供了其他参数,必须同时提供固体目标重量")
|
||||
if not percent_10_1_volume:
|
||||
raise BioyondException("10%_1物料:如果提供了其他参数,必须同时提供液体体积")
|
||||
if not percent_10_1_liquid_material_name:
|
||||
raise BioyondException("10%_1物料:如果提供了其他参数,必须同时提供液体物料名称")
|
||||
|
||||
# 10%_2物料:如果有物料名称、目标重量、体积或液体物料名称中的任何一个,就必须有全部参数
|
||||
if any([percent_10_2_assign_material_name, percent_10_2_target_weigh, percent_10_2_volume, percent_10_2_liquid_material_name]):
|
||||
if not percent_10_2_assign_material_name:
|
||||
raise BioyondException("10%_2物料:如果提供了其他参数,必须同时提供固体物料名称")
|
||||
if not percent_10_2_target_weigh:
|
||||
raise BioyondException("10%_2物料:如果提供了其他参数,必须同时提供固体目标重量")
|
||||
if not percent_10_2_volume:
|
||||
raise BioyondException("10%_2物料:如果提供了其他参数,必须同时提供液体体积")
|
||||
if not percent_10_2_liquid_material_name:
|
||||
raise BioyondException("10%_2物料:如果提供了其他参数,必须同时提供液体物料名称")
|
||||
|
||||
# 10%_3物料:如果有物料名称、目标重量、体积或液体物料名称中的任何一个,就必须有全部参数
|
||||
if any([percent_10_3_assign_material_name, percent_10_3_target_weigh, percent_10_3_volume, percent_10_3_liquid_material_name]):
|
||||
if not percent_10_3_assign_material_name:
|
||||
raise BioyondException("10%_3物料:如果提供了其他参数,必须同时提供固体物料名称")
|
||||
if not percent_10_3_target_weigh:
|
||||
raise BioyondException("10%_3物料:如果提供了其他参数,必须同时提供固体目标重量")
|
||||
if not percent_10_3_volume:
|
||||
raise BioyondException("10%_3物料:如果提供了其他参数,必须同时提供液体体积")
|
||||
if not percent_10_3_liquid_material_name:
|
||||
raise BioyondException("10%_3物料:如果提供了其他参数,必须同时提供液体物料名称")
|
||||
|
||||
# 2. 生成任务编码和设置默认值
|
||||
order_code = "task_vial_" + str(int(datetime.now().timestamp()))
|
||||
if order_name is None:
|
||||
order_name = "90%10%小瓶投料任务"
|
||||
if speed is None:
|
||||
speed = "400"
|
||||
if temperature is None:
|
||||
temperature = "40"
|
||||
if delay_time is None:
|
||||
delay_time = "600"
|
||||
|
||||
# 3. 工作流ID
|
||||
workflow_id = "3a19310d-16b9-9d81-b109-0748e953694b"
|
||||
|
||||
# 4. 查询工作流对应的holdMID
|
||||
material_info = self.hardware_interface.material_id_query(workflow_id)
|
||||
if not material_info:
|
||||
raise BioyondException(f"无法查询工作流 {workflow_id} 的物料信息")
|
||||
|
||||
# 获取locations列表
|
||||
locations = material_info.get("locations", []) if isinstance(material_info, dict) else []
|
||||
if not locations:
|
||||
raise BioyondException(f"工作流 {workflow_id} 没有找到库位信息")
|
||||
|
||||
# 查找指定名称的库位
|
||||
hold_mid = None
|
||||
for location in locations:
|
||||
if location.get("holdMName") == hold_m_name:
|
||||
hold_mid = location.get("holdMId")
|
||||
break
|
||||
|
||||
if not hold_mid:
|
||||
raise BioyondException(f"未找到库位名称为 {hold_m_name} 的库位,请检查名称是否正确")
|
||||
|
||||
extend_properties = f"{{\"{ hold_mid }\": {{}}}}"
|
||||
self.hardware_interface._logger.info(f"找到库位 {hold_m_name} 对应的holdMId: {hold_mid}")
|
||||
|
||||
# 5. 构建任务参数
|
||||
order_data = {
|
||||
"orderCode": order_code,
|
||||
"orderName": order_name,
|
||||
"workflowId": workflow_id,
|
||||
"borderNumber": 1,
|
||||
"paramValues": {},
|
||||
"ExtendProperties": extend_properties
|
||||
}
|
||||
|
||||
# 添加搅拌参数
|
||||
order_data["paramValues"]["e8264e47-c319-d9d9-8676-4dd5cb382b11"] = [
|
||||
{"m": 0, "n": 3, "Key": "speed", "Value": speed},
|
||||
{"m": 0, "n": 3, "Key": "temperature", "Value": temperature}
|
||||
]
|
||||
|
||||
# 添加延迟时间参数
|
||||
order_data["paramValues"]["dc5dba79-5e4b-8eae-cbc5-e93482e43b1f"] = [
|
||||
{"m": 0, "n": 4, "Key": "DelayTime", "Value": delay_time}
|
||||
]
|
||||
|
||||
# 添加90%_1参数
|
||||
if percent_90_1_assign_material_name is not None and percent_90_1_target_weigh is not None:
|
||||
order_data["paramValues"]["e7d3c0a3-25c2-c42d-c84b-860c4a5ef844"] = [
|
||||
{"m": 15, "n": 1, "Key": "targetWeigh", "Value": percent_90_1_target_weigh},
|
||||
{"m": 15, "n": 1, "Key": "assignMaterialName", "Value": percent_90_1_assign_material_name}
|
||||
]
|
||||
|
||||
# 添加90%_2参数
|
||||
if percent_90_2_assign_material_name is not None and percent_90_2_target_weigh is not None:
|
||||
order_data["paramValues"]["50b912c4-6c81-0734-1c8b-532428b2a4a5"] = [
|
||||
{"m": 18, "n": 1, "Key": "targetWeigh", "Value": percent_90_2_target_weigh},
|
||||
{"m": 18, "n": 1, "Key": "assignMaterialName", "Value": percent_90_2_assign_material_name}
|
||||
]
|
||||
|
||||
# 添加90%_3参数
|
||||
if percent_90_3_assign_material_name is not None and percent_90_3_target_weigh is not None:
|
||||
order_data["paramValues"]["9c3674b3-c7cb-946e-fa03-fa2861d8aec4"] = [
|
||||
{"m": 21, "n": 1, "Key": "targetWeigh", "Value": percent_90_3_target_weigh},
|
||||
{"m": 21, "n": 1, "Key": "assignMaterialName", "Value": percent_90_3_assign_material_name}
|
||||
]
|
||||
|
||||
# 添加10%_1固体参数
|
||||
if percent_10_1_assign_material_name is not None and percent_10_1_target_weigh is not None:
|
||||
order_data["paramValues"]["73a0bfd8-1967-45e9-4bab-c07ccd1a2727"] = [
|
||||
{"m": 3, "n": 1, "Key": "targetWeigh", "Value": percent_10_1_target_weigh},
|
||||
{"m": 3, "n": 1, "Key": "assignMaterialName", "Value": percent_10_1_assign_material_name}
|
||||
]
|
||||
|
||||
# 添加10%_1液体参数
|
||||
if percent_10_1_liquid_material_name is not None and percent_10_1_volume is not None:
|
||||
order_data["paramValues"]["39634d40-c623-473a-8e5f-bc301aca2522"] = [
|
||||
{"m": 3, "n": 3, "Key": "volume", "Value": percent_10_1_volume},
|
||||
{"m": 3, "n": 3, "Key": "assignMaterialName", "Value": percent_10_1_liquid_material_name}
|
||||
]
|
||||
|
||||
# 添加10%_2固体参数
|
||||
if percent_10_2_assign_material_name is not None and percent_10_2_target_weigh is not None:
|
||||
order_data["paramValues"]["2d9c16fa-2a19-cd47-a67b-3cadff9e3e3d"] = [
|
||||
{"m": 7, "n": 1, "Key": "targetWeigh", "Value": percent_10_2_target_weigh},
|
||||
{"m": 7, "n": 1, "Key": "assignMaterialName", "Value": percent_10_2_assign_material_name}
|
||||
]
|
||||
|
||||
# 添加10%_2液体参数
|
||||
if percent_10_2_liquid_material_name is not None and percent_10_2_volume is not None:
|
||||
order_data["paramValues"]["e60541bb-ed68-e839-7305-2b4abe38a13d"] = [
|
||||
{"m": 7, "n": 3, "Key": "volume", "Value": percent_10_2_volume},
|
||||
{"m": 7, "n": 3, "Key": "assignMaterialName", "Value": percent_10_2_liquid_material_name}
|
||||
]
|
||||
|
||||
# 添加10%_3固体参数
|
||||
if percent_10_3_assign_material_name is not None and percent_10_3_target_weigh is not None:
|
||||
order_data["paramValues"]["27494733-0f71-a916-7cd2-1929a0125f17"] = [
|
||||
{"m": 11, "n": 1, "Key": "targetWeigh", "Value": percent_10_3_target_weigh},
|
||||
{"m": 11, "n": 1, "Key": "assignMaterialName", "Value": percent_10_3_assign_material_name}
|
||||
]
|
||||
|
||||
# 添加10%_3液体参数
|
||||
if percent_10_3_liquid_material_name is not None and percent_10_3_volume is not None:
|
||||
order_data["paramValues"]["c8798c29-786f-6858-7d7f-5330b890f2a6"] = [
|
||||
{"m": 11, "n": 3, "Key": "volume", "Value": percent_10_3_volume},
|
||||
{"m": 11, "n": 3, "Key": "assignMaterialName", "Value": percent_10_3_liquid_material_name}
|
||||
]
|
||||
|
||||
# 6. 转换为JSON字符串并创建任务
|
||||
json_str = json.dumps([order_data], ensure_ascii=False)
|
||||
self.hardware_interface._logger.info(f"创建90%10%小瓶投料任务参数: {json_str}")
|
||||
|
||||
# 7. 调用create_order方法创建任务
|
||||
result = self.hardware_interface.create_order(json_str)
|
||||
self.hardware_interface._logger.info(f"创建90%10%小瓶投料任务结果: {result}")
|
||||
return json.dumps({"suc": True})
|
||||
|
||||
except BioyondException:
|
||||
# 重新抛出BioyondException
|
||||
raise
|
||||
except Exception as e:
|
||||
# 捕获其他未预期的异常,转换为BioyondException
|
||||
error_msg = f"创建90%10%小瓶投料任务时发生未预期的错误: {str(e)}"
|
||||
self.hardware_interface._logger.error(error_msg)
|
||||
raise BioyondException(error_msg)
|
||||
|
||||
# 二胺溶液配置任务创建方法
|
||||
def create_diamine_solution_task(self,
|
||||
order_name: str = None,
|
||||
material_name: str = None,
|
||||
target_weigh: str = None,
|
||||
volume: str = None,
|
||||
liquid_material_name: str = "NMP",
|
||||
speed: str = None,
|
||||
temperature: str = None,
|
||||
delay_time: str = None,
|
||||
hold_m_name: str = None) -> dict:
|
||||
"""
|
||||
创建二胺溶液配置任务
|
||||
|
||||
参数说明:
|
||||
- order_name: 任务名称,如果为None则使用默认名称
|
||||
- material_name: 固体物料名称,必填
|
||||
- target_weigh: 固体目标重量,必填
|
||||
- volume: 液体体积,必填
|
||||
- liquid_material_name: 液体物料名称,默认为NMP
|
||||
- speed: 搅拌速度,如果为None则使用默认值400
|
||||
- temperature: 温度,如果为None则使用默认值20
|
||||
- delay_time: 延迟时间,如果为None则使用默认值600
|
||||
- hold_m_name: 库位名称,如"ODA-1",用于查找对应的holdMId
|
||||
|
||||
返回: 任务创建结果
|
||||
|
||||
异常:
|
||||
- BioyondException: 各种错误情况下的统一异常
|
||||
"""
|
||||
try:
|
||||
# 1. 参数验证
|
||||
if not material_name:
|
||||
raise BioyondException("material_name 是必填参数")
|
||||
if not target_weigh:
|
||||
raise BioyondException("target_weigh 是必填参数")
|
||||
if not volume:
|
||||
raise BioyondException("volume 是必填参数")
|
||||
if not hold_m_name:
|
||||
raise BioyondException("hold_m_name 是必填参数")
|
||||
|
||||
|
||||
# 2. 生成任务编码和设置默认值
|
||||
order_code = "task_oda_" + str(int(datetime.now().timestamp()))
|
||||
if order_name is None:
|
||||
order_name = f"二胺溶液配置-{material_name}"
|
||||
if speed is None:
|
||||
speed = "400"
|
||||
if temperature is None:
|
||||
temperature = "20"
|
||||
if delay_time is None:
|
||||
delay_time = "600"
|
||||
|
||||
# 3. 工作流ID - 二胺溶液配置工作流
|
||||
workflow_id = "3a15d4a1-3bbe-76f9-a458-292896a338f5"
|
||||
|
||||
# 4. 查询工作流对应的holdMID
|
||||
material_info = self.material_id_query(workflow_id)
|
||||
if not material_info:
|
||||
raise BioyondException(f"无法查询工作流 {workflow_id} 的物料信息")
|
||||
|
||||
# 获取locations列表
|
||||
locations = material_info.get("locations", []) if isinstance(material_info, dict) else []
|
||||
if not locations:
|
||||
raise BioyondException(f"工作流 {workflow_id} 没有找到库位信息")
|
||||
|
||||
# 查找指定名称的库位
|
||||
hold_mid = None
|
||||
for location in locations:
|
||||
if location.get("holdMName") == hold_m_name:
|
||||
hold_mid = location.get("holdMId")
|
||||
break
|
||||
|
||||
if not hold_mid:
|
||||
raise BioyondException(f"未找到库位名称为 {hold_m_name} 的库位,请检查名称是否正确")
|
||||
|
||||
extend_properties = f"{{\"{ hold_mid }\": {{}}}}"
|
||||
self.hardware_interface._logger.info(f"找到库位 {hold_m_name} 对应的holdMId: {hold_mid}")
|
||||
|
||||
# 5. 构建任务参数
|
||||
order_data = {
|
||||
"orderCode": order_code,
|
||||
"orderName": order_name,
|
||||
"workflowId": workflow_id,
|
||||
"borderNumber": 1,
|
||||
"paramValues": {
|
||||
# 固体物料参数
|
||||
"3a15d4a1-3bde-f5bc-053f-1ae0bf1f357e": [
|
||||
{"m": 3, "n": 2, "Key": "targetWeigh", "Value": target_weigh},
|
||||
{"m": 3, "n": 2, "Key": "assignMaterialName", "Value": material_name}
|
||||
],
|
||||
# 液体物料参数
|
||||
"3a15d4a1-3bde-d584-b309-e661ae8f1c01": [
|
||||
{"m": 3, "n": 3, "Key": "volume", "Value": volume},
|
||||
{"m": 3, "n": 3, "Key": "assignMaterialName", "Value": liquid_material_name}
|
||||
],
|
||||
# 搅拌参数
|
||||
"3a15d4a1-3bde-8ec4-1ced-92efc97ed73d": [
|
||||
{"m": 3, "n": 6, "Key": "speed", "Value": speed},
|
||||
{"m": 3, "n": 6, "Key": "temperature", "Value": temperature}
|
||||
],
|
||||
# 延迟时间参数
|
||||
"3a15d4a1-3bde-3b92-83ff-8923a0addbbc": [
|
||||
{"m": 3, "n": 7, "Key": "DelayTime", "Value": delay_time}
|
||||
]
|
||||
},
|
||||
"ExtendProperties": extend_properties
|
||||
}
|
||||
|
||||
# 6. 转换为JSON字符串并创建任务
|
||||
json_str = json.dumps([order_data], ensure_ascii=False)
|
||||
self.hardware_interface._logger.info(f"创建二胺溶液配置任务参数: {json_str}")
|
||||
|
||||
# 7. 调用create_order方法创建任务
|
||||
result = self.hardware_interface.create_order(json_str)
|
||||
self.hardware_interface._logger.info(f"创建二胺溶液配置任务结果: {result}")
|
||||
|
||||
return json.dumps({"suc": True})
|
||||
|
||||
except BioyondException:
|
||||
# 重新抛出BioyondException
|
||||
raise
|
||||
except Exception as e:
|
||||
# 捕获其他未预期的异常,转换为BioyondException
|
||||
error_msg = f"创建二胺溶液配置任务时发生未预期的错误: {str(e)}"
|
||||
self.hardware_interface._logger.error(error_msg)
|
||||
raise BioyondException(error_msg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
bioyond = BioyondDispensingStation(config={
|
||||
"api_key": "DE9BDDA0",
|
||||
"api_host": "http://192.168.1.200:44388"
|
||||
})
|
||||
|
||||
# 示例1:使用material_id_query查询工作流对应的holdMID
|
||||
workflow_id_1 = "3a15d4a1-3bbe-76f9-a458-292896a338f5" # 二胺溶液配置工作流ID
|
||||
workflow_id_2 = "3a19310d-16b9-9d81-b109-0748e953694b" # 90%10%小瓶投料工作流ID
|
||||
|
||||
#示例2:创建二胺溶液配置任务 - ODA,指定库位名称
|
||||
# bioyond.create_diamine_solution_task(
|
||||
# order_code="task_oda_" + str(int(datetime.now().timestamp())),
|
||||
# order_name="二胺溶液配置-ODA",
|
||||
# material_name="ODA-1",
|
||||
# target_weigh="12.000",
|
||||
# volume="60",
|
||||
# liquid_material_name= "NMP",
|
||||
# speed="400",
|
||||
# temperature="20",
|
||||
# delay_time="600",
|
||||
# hold_m_name="烧杯ODA"
|
||||
# )
|
||||
|
||||
# bioyond.create_diamine_solution_task(
|
||||
# order_code="task_pda_" + str(int(datetime.now().timestamp())),
|
||||
# order_name="二胺溶液配置-PDA",
|
||||
# material_name="PDA-1",
|
||||
# target_weigh="4.178",
|
||||
# volume="60",
|
||||
# liquid_material_name= "NMP",
|
||||
# speed="400",
|
||||
# temperature="20",
|
||||
# delay_time="600",
|
||||
# hold_m_name="烧杯PDA-2"
|
||||
# )
|
||||
|
||||
# bioyond.create_diamine_solution_task(
|
||||
# order_code="task_mpda_" + str(int(datetime.now().timestamp())),
|
||||
# order_name="二胺溶液配置-MPDA",
|
||||
# material_name="MPDA-1",
|
||||
# target_weigh="3.298",
|
||||
# volume="50",
|
||||
# liquid_material_name= "NMP",
|
||||
# speed="400",
|
||||
# temperature="20",
|
||||
# delay_time="600",
|
||||
# hold_m_name="烧杯MPDA"
|
||||
# )
|
||||
|
||||
bioyond.material_id_query("3a19310d-16b9-9d81-b109-0748e953694b")
|
||||
bioyond.material_id_query("3a15d4a1-3bbe-76f9-a458-292896a338f5")
|
||||
|
||||
|
||||
#示例4:创建90%10%小瓶投料任务
|
||||
# vial_result = bioyond.create_90_10_vial_feeding_task(
|
||||
# order_code="task_vial_" + str(int(datetime.now().timestamp())),
|
||||
# order_name="90%10%小瓶投料-1",
|
||||
# percent_90_1_assign_material_name="BTDA-1",
|
||||
# percent_90_1_target_weigh="7.392",
|
||||
# percent_90_2_assign_material_name="BTDA-1",
|
||||
# percent_90_2_target_weigh="7.392",
|
||||
# percent_90_3_assign_material_name="BTDA-2",
|
||||
# percent_90_3_target_weigh="7.392",
|
||||
# percent_10_1_assign_material_name="BTDA-2",
|
||||
# percent_10_1_target_weigh="1.500",
|
||||
# percent_10_1_volume="20",
|
||||
# percent_10_1_liquid_material_name="NMP",
|
||||
# # percent_10_2_assign_material_name="BTDA-c",
|
||||
# # percent_10_2_target_weigh="1.2",
|
||||
# # percent_10_2_volume="20",
|
||||
# # percent_10_2_liquid_material_name="NMP",
|
||||
# speed="400",
|
||||
# temperature="60",
|
||||
# delay_time="1200",
|
||||
# hold_m_name="8.4分装板-1"
|
||||
# )
|
||||
|
||||
# vial_result = bioyond.create_90_10_vial_feeding_task(
|
||||
# order_code="task_vial_" + str(int(datetime.now().timestamp())),
|
||||
# order_name="90%10%小瓶投料-2",
|
||||
# percent_90_1_assign_material_name="BPDA-1",
|
||||
# percent_90_1_target_weigh="5.006",
|
||||
# percent_90_2_assign_material_name="PMDA-1",
|
||||
# percent_90_2_target_weigh="3.810",
|
||||
# percent_90_3_assign_material_name="BPDA-1",
|
||||
# percent_90_3_target_weigh="8.399",
|
||||
# percent_10_1_assign_material_name="BPDA-1",
|
||||
# percent_10_1_target_weigh="1.200",
|
||||
# percent_10_1_volume="20",
|
||||
# percent_10_1_liquid_material_name="NMP",
|
||||
# percent_10_2_assign_material_name="BPDA-1",
|
||||
# percent_10_2_target_weigh="1.200",
|
||||
# percent_10_2_volume="20",
|
||||
# percent_10_2_liquid_material_name="NMP",
|
||||
# speed="400",
|
||||
# temperature="60",
|
||||
# delay_time="1200",
|
||||
# hold_m_name="8.4分装板-2"
|
||||
# )
|
||||
|
||||
#启动调度器
|
||||
#bioyond.scheduler_start()
|
||||
|
||||
#继续调度器
|
||||
#bioyond.scheduler_continue()
|
||||
|
||||
result0 = bioyond.stock_material('{"typeMode": 0, "includeDetail": true}')
|
||||
result1 = bioyond.stock_material('{"typeMode": 1, "includeDetail": true}')
|
||||
result2 = bioyond.stock_material('{"typeMode": 2, "includeDetail": true}')
|
||||
|
||||
matpos1 = bioyond.query_warehouse_by_material_type("3a14196e-b7a0-a5da-1931-35f3000281e9")
|
||||
matpos2 = bioyond.query_warehouse_by_material_type("3a14196e-5dfe-6e21-0c79-fe2036d052c4")
|
||||
matpos3 = bioyond.query_warehouse_by_material_type("3a14196b-24f2-ca49-9081-0cab8021bf1a")
|
||||
|
||||
#样品板(里面有样品瓶)
|
||||
material_data_yp = {
|
||||
"typeId": "3a14196e-b7a0-a5da-1931-35f3000281e9",
|
||||
#"code": "物料编码001",
|
||||
#"barCode": "物料条码001",
|
||||
"name": "8.4样品板",
|
||||
"unit": "个",
|
||||
"quantity": 1,
|
||||
"details": [
|
||||
{
|
||||
"typeId": "3a14196a-cf7d-8aea-48d8-b9662c7dba94",
|
||||
#"code": "物料编码001",
|
||||
"name": "BTDA-1",
|
||||
"quantity": 20,
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
},
|
||||
{
|
||||
"typeId": "3a14196a-cf7d-8aea-48d8-b9662c7dba94",
|
||||
#"code": "物料编码001",
|
||||
"name": "BPDA-1",
|
||||
"quantity": 20,
|
||||
"x": 2,
|
||||
"y": 1, #x1y2是A02
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
},
|
||||
{
|
||||
"typeId": "3a14196a-cf7d-8aea-48d8-b9662c7dba94",
|
||||
#"code": "物料编码001",
|
||||
"name": "BTDA-2",
|
||||
"quantity": 20,
|
||||
"x": 1,
|
||||
"y": 2, #x1y2是A02
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
},
|
||||
{
|
||||
"typeId": "3a14196a-cf7d-8aea-48d8-b9662c7dba94",
|
||||
#"code": "物料编码001",
|
||||
"name": "PMDA-1",
|
||||
"quantity": 20,
|
||||
"x": 2,
|
||||
"y": 2, #x1y2是A02
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
}
|
||||
],
|
||||
"Parameters":"{}"
|
||||
}
|
||||
|
||||
material_data_yp = {
|
||||
"typeId": "3a14196e-b7a0-a5da-1931-35f3000281e9",
|
||||
#"code": "物料编码001",
|
||||
#"barCode": "物料条码001",
|
||||
"name": "8.7样品板",
|
||||
"unit": "个",
|
||||
"quantity": 1,
|
||||
"details": [
|
||||
{
|
||||
"typeId": "3a14196a-cf7d-8aea-48d8-b9662c7dba94",
|
||||
#"code": "物料编码001",
|
||||
"name": "mianfen",
|
||||
"quantity": 13,
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
},
|
||||
{
|
||||
"typeId": "3a14196a-cf7d-8aea-48d8-b9662c7dba94",
|
||||
#"code": "物料编码001",
|
||||
"name": "mianfen2",
|
||||
"quantity": 13,
|
||||
"x": 1,
|
||||
"y": 2, #x1y2是A02
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
}
|
||||
],
|
||||
"Parameters":"{}"
|
||||
}
|
||||
|
||||
#分装板
|
||||
material_data_fzb_1 = {
|
||||
"typeId": "3a14196e-5dfe-6e21-0c79-fe2036d052c4",
|
||||
#"code": "物料编码001",
|
||||
#"barCode": "物料条码001",
|
||||
"name": "8.7分装板",
|
||||
"unit": "个",
|
||||
"quantity": 1,
|
||||
"details": [
|
||||
{
|
||||
"typeId": "3a14196c-76be-2279-4e22-7310d69aed68",
|
||||
#"code": "物料编码001",
|
||||
"name": "10%小瓶1",
|
||||
"quantity": 1,
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
},
|
||||
{
|
||||
"typeId": "3a14196c-76be-2279-4e22-7310d69aed68",
|
||||
#"code": "物料编码001",
|
||||
"name": "10%小瓶2",
|
||||
"quantity": 1,
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
},
|
||||
{
|
||||
"typeId": "3a14196c-76be-2279-4e22-7310d69aed68",
|
||||
#"code": "物料编码001",
|
||||
"name": "10%小瓶3",
|
||||
"quantity": 1,
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
},
|
||||
{
|
||||
"typeId": "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea",
|
||||
#"code": "物料编码001",
|
||||
"name": "90%小瓶1",
|
||||
"quantity": 1,
|
||||
"x": 2,
|
||||
"y": 1, #x1y2是A02
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
},
|
||||
{
|
||||
"typeId": "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea",
|
||||
#"code": "物料编码001",
|
||||
"name": "90%小瓶2",
|
||||
"quantity": 1,
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
},
|
||||
{
|
||||
"typeId": "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea",
|
||||
#"code": "物料编码001",
|
||||
"name": "90%小瓶3",
|
||||
"quantity": 1,
|
||||
"x": 2,
|
||||
"y": 3,
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
}
|
||||
],
|
||||
"Parameters":"{}"
|
||||
}
|
||||
|
||||
material_data_fzb_2 = {
|
||||
"typeId": "3a14196e-5dfe-6e21-0c79-fe2036d052c4",
|
||||
#"code": "物料编码001",
|
||||
#"barCode": "物料条码001",
|
||||
"name": "8.4分装板-2",
|
||||
"unit": "个",
|
||||
"quantity": 1,
|
||||
"details": [
|
||||
{
|
||||
"typeId": "3a14196c-76be-2279-4e22-7310d69aed68",
|
||||
#"code": "物料编码001",
|
||||
"name": "10%小瓶1",
|
||||
"quantity": 1,
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
},
|
||||
{
|
||||
"typeId": "3a14196c-76be-2279-4e22-7310d69aed68",
|
||||
#"code": "物料编码001",
|
||||
"name": "10%小瓶2",
|
||||
"quantity": 1,
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
},
|
||||
{
|
||||
"typeId": "3a14196c-76be-2279-4e22-7310d69aed68",
|
||||
#"code": "物料编码001",
|
||||
"name": "10%小瓶3",
|
||||
"quantity": 1,
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
},
|
||||
{
|
||||
"typeId": "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea",
|
||||
#"code": "物料编码001",
|
||||
"name": "90%小瓶1",
|
||||
"quantity": 1,
|
||||
"x": 2,
|
||||
"y": 1, #x1y2是A02
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
},
|
||||
{
|
||||
"typeId": "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea",
|
||||
#"code": "物料编码001",
|
||||
"name": "90%小瓶2",
|
||||
"quantity": 1,
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
#"unit": "单位"
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
},
|
||||
{
|
||||
"typeId": "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea",
|
||||
#"code": "物料编码001",
|
||||
"name": "90%小瓶3",
|
||||
"quantity": 1,
|
||||
"x": 2,
|
||||
"y": 3,
|
||||
"molecular": 1,
|
||||
"Parameters":"{\"molecular\": 1}"
|
||||
}
|
||||
],
|
||||
"Parameters":"{}"
|
||||
}
|
||||
|
||||
#烧杯
|
||||
material_data_sb_oda = {
|
||||
"typeId": "3a14196b-24f2-ca49-9081-0cab8021bf1a",
|
||||
#"code": "物料编码001",
|
||||
#"barCode": "物料条码001",
|
||||
"name": "mianfen1",
|
||||
"unit": "个",
|
||||
"quantity": 1,
|
||||
"Parameters":"{}"
|
||||
}
|
||||
|
||||
material_data_sb_pda_2 = {
|
||||
"typeId": "3a14196b-24f2-ca49-9081-0cab8021bf1a",
|
||||
#"code": "物料编码001",
|
||||
#"barCode": "物料条码001",
|
||||
"name": "mianfen2",
|
||||
"unit": "个",
|
||||
"quantity": 1,
|
||||
"Parameters":"{}"
|
||||
}
|
||||
|
||||
# material_data_sb_mpda = {
|
||||
# "typeId": "3a14196b-24f2-ca49-9081-0cab8021bf1a",
|
||||
# #"code": "物料编码001",
|
||||
# #"barCode": "物料条码001",
|
||||
# "name": "烧杯MPDA",
|
||||
# "unit": "个",
|
||||
# "quantity": 1,
|
||||
# "Parameters":"{}"
|
||||
# }
|
||||
|
||||
|
||||
#result_1 = bioyond.add_material(json.dumps(material_data_yp, ensure_ascii=False))
|
||||
#result_2 = bioyond.add_material(json.dumps(material_data_fzb_1, ensure_ascii=False))
|
||||
# result_3 = bioyond.add_material(json.dumps(material_data_fzb_2, ensure_ascii=False))
|
||||
# result_4 = bioyond.add_material(json.dumps(material_data_sb_oda, ensure_ascii=False))
|
||||
# result_5 = bioyond.add_material(json.dumps(material_data_sb_pda_2, ensure_ascii=False))
|
||||
# #result会返回id
|
||||
# #样品板1id:3a1b3e7d-339d-0291-dfd3-13e2a78fe521
|
||||
|
||||
|
||||
# #将指定物料入库到指定库位
|
||||
#bioyond.material_inbound(result_1, "3a14198e-6929-31f0-8a22-0f98f72260df")
|
||||
#bioyond.material_inbound(result_2, "3a14198e-6929-46fe-841e-03dd753f1e4a")
|
||||
# bioyond.material_inbound(result_3, "3a14198e-6929-72ac-32ce-9b50245682b8")
|
||||
# bioyond.material_inbound(result_4, "3a14198e-d724-e036-afdc-2ae39a7f3383")
|
||||
# bioyond.material_inbound(result_5, "3a14198e-d724-d818-6d4f-5725191a24b5")
|
||||
|
||||
#bioyond.material_outbound(result_1, "3a14198e-6929-31f0-8a22-0f98f72260df")
|
||||
|
||||
# bioyond.stock_material('{"typeMode": 2, "includeDetail": true}')
|
||||
|
||||
query_order = {"status":"100", "pageCount": "10"}
|
||||
bioyond.order_query(json.dumps(query_order, ensure_ascii=False))
|
||||
|
||||
# id = "3a1bce3c-4f31-c8f3-5525-f3b273bc34dc"
|
||||
# bioyond.sample_waste_removal(id)
|
||||
|
||||
207
unilabos/devices/workstation/bioyond_studio/reaction_station.py
Normal file
207
unilabos/devices/workstation/bioyond_studio/reaction_station.py
Normal file
@@ -0,0 +1,207 @@
|
||||
import json
|
||||
|
||||
from unilabos.devices.workstation.bioyond_studio.station import BioyondWorkstation
|
||||
from unilabos.devices.workstation.bioyond_studio.config import (
|
||||
API_CONFIG, WORKFLOW_MAPPINGS, WORKFLOW_STEP_IDS, MATERIAL_TYPE_MAPPINGS,
|
||||
STATION_TYPES, DEFAULT_STATION_CONFIG
|
||||
)
|
||||
|
||||
|
||||
class BioyondReactionStation(BioyondWorkstation):
|
||||
def __init__(self, config: dict = None):
|
||||
super().__init__(config)
|
||||
|
||||
# 工作流方法
|
||||
def reactor_taken_out(self):
|
||||
"""反应器取出"""
|
||||
self.hardware_interface.append_to_workflow_sequence('{"web_workflow_name": "reactor_taken_out"}')
|
||||
reactor_taken_out_params = {"param_values": {}}
|
||||
self.hardware_interface.pending_task_params.append(reactor_taken_out_params)
|
||||
print(f"成功添加反应器取出工作流")
|
||||
print(f"当前队列长度: {len(self.hardware_interface.pending_task_params)}")
|
||||
return json.dumps({"suc": True})
|
||||
|
||||
def reactor_taken_in(self, assign_material_name: str, cutoff: str = "900000", temperature: float = -10.00):
|
||||
"""反应器放入"""
|
||||
self.append_to_workflow_sequence('{"web_workflow_name": "reactor_taken_in"}')
|
||||
material_id = self._get_material_id_by_name(assign_material_name)
|
||||
|
||||
if isinstance(temperature, str):
|
||||
temperature = float(temperature)
|
||||
|
||||
step_id = WORKFLOW_STEP_IDS["reactor_taken_in"]["config"]
|
||||
reactor_taken_in_params = {
|
||||
"param_values": {
|
||||
step_id: [
|
||||
{"m": 0, "n": 3, "Key": "cutoff", "Value": cutoff},
|
||||
{"m": 0, "n": 3, "Key": "temperature", "Value": f"{temperature:.2f}"},
|
||||
{"m": 0, "n": 3, "Key": "assignMaterialName", "Value": material_id}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
self.pending_task_params.append(reactor_taken_in_params)
|
||||
print(f"成功添加反应器放入参数: material={assign_material_name}->ID:{material_id}, cutoff={cutoff}, temp={temperature:.2f}")
|
||||
print(f"当前队列长度: {len(self.pending_task_params)}")
|
||||
return json.dumps({"suc": True})
|
||||
|
||||
def solid_feeding_vials(self, material_id: str, time: str = "0", torque_variation: str = "1",
|
||||
assign_material_name: str = None, temperature: float = 25.00):
|
||||
"""固体进料小瓶"""
|
||||
self.append_to_workflow_sequence('{"web_workflow_name": "Solid_feeding_vials"}')
|
||||
material_id_m = self._get_material_id_by_name(assign_material_name)
|
||||
|
||||
if isinstance(temperature, str):
|
||||
temperature = float(temperature)
|
||||
|
||||
feeding_id = WORKFLOW_STEP_IDS["solid_feeding_vials"]["feeding"]
|
||||
observe_id = WORKFLOW_STEP_IDS["solid_feeding_vials"]["observe"]
|
||||
|
||||
solid_feeding_vials_params = {
|
||||
"param_values": {
|
||||
feeding_id: [
|
||||
{"m": 0, "n": 3, "Key": "materialId", "Value": material_id},
|
||||
{"m": 0, "n": 3, "Key": "assignMaterialName", "Value": material_id_m}
|
||||
],
|
||||
observe_id: [
|
||||
{"m": 1, "n": 0, "Key": "time", "Value": time},
|
||||
{"m": 1, "n": 0, "Key": "torqueVariation", "Value": torque_variation},
|
||||
{"m": 1, "n": 0, "Key": "temperature", "Value": f"{temperature:.2f}"}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
self.pending_task_params.append(solid_feeding_vials_params)
|
||||
print(f"成功添加固体进料小瓶参数: material_id={material_id}, time={time}min, temp={temperature:.2f}°C")
|
||||
print(f"当前队列长度: {len(self.pending_task_params)}")
|
||||
return json.dumps({"suc": True})
|
||||
|
||||
def liquid_feeding_vials_non_titration(self, volumeFormula: str, assign_material_name: str,
|
||||
titration_type: str = "1", time: str = "0",
|
||||
torque_variation: str = "1", temperature: float = 25.00):
|
||||
"""液体进料小瓶(非滴定)"""
|
||||
self.append_to_workflow_sequence('{"web_workflow_name": "Liquid_feeding_vials(non-titration)"}')
|
||||
material_id = self._get_material_id_by_name(assign_material_name)
|
||||
|
||||
if isinstance(temperature, str):
|
||||
temperature = float(temperature)
|
||||
|
||||
liquid_id = WORKFLOW_STEP_IDS["liquid_feeding_vials_non_titration"]["liquid"]
|
||||
observe_id = WORKFLOW_STEP_IDS["liquid_feeding_vials_non_titration"]["observe"]
|
||||
|
||||
params = {
|
||||
"param_values": {
|
||||
liquid_id: [
|
||||
{"m": 0, "n": 3, "Key": "volumeFormula", "Value": volumeFormula},
|
||||
{"m": 0, "n": 3, "Key": "assignMaterialName", "Value": material_id},
|
||||
{"m": 0, "n": 3, "Key": "titrationType", "Value": titration_type}
|
||||
],
|
||||
observe_id: [
|
||||
{"m": 1, "n": 0, "Key": "time", "Value": time},
|
||||
{"m": 1, "n": 0, "Key": "torqueVariation", "Value": torque_variation},
|
||||
{"m": 1, "n": 0, "Key": "temperature", "Value": f"{temperature:.2f}"}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
self.pending_task_params.append(params)
|
||||
print(f"成功添加液体进料小瓶(非滴定)参数: volume={volumeFormula}μL, material={assign_material_name}->ID:{material_id}")
|
||||
print(f"当前队列长度: {len(self.pending_task_params)}")
|
||||
return json.dumps({"suc": True})
|
||||
|
||||
def liquid_feeding_solvents(self, assign_material_name: str, volume: str, titration_type: str = "1",
|
||||
time: str = "360", torque_variation: str = "2", temperature: float = 25.00):
|
||||
"""液体进料溶剂"""
|
||||
self.append_to_workflow_sequence('{"web_workflow_name": "Liquid_feeding_solvents"}')
|
||||
material_id = self._get_material_id_by_name(assign_material_name)
|
||||
|
||||
if isinstance(temperature, str):
|
||||
temperature = float(temperature)
|
||||
|
||||
liquid_id = WORKFLOW_STEP_IDS["liquid_feeding_solvents"]["liquid"]
|
||||
observe_id = WORKFLOW_STEP_IDS["liquid_feeding_solvents"]["observe"]
|
||||
|
||||
params = {
|
||||
"param_values": {
|
||||
liquid_id: [
|
||||
{"m": 0, "n": 1, "Key": "titrationType", "Value": titration_type},
|
||||
{"m": 0, "n": 1, "Key": "volume", "Value": volume},
|
||||
{"m": 0, "n": 1, "Key": "assignMaterialName", "Value": material_id}
|
||||
],
|
||||
observe_id: [
|
||||
{"m": 1, "n": 0, "Key": "time", "Value": time},
|
||||
{"m": 1, "n": 0, "Key": "torqueVariation", "Value": torque_variation},
|
||||
{"m": 1, "n": 0, "Key": "temperature", "Value": f"{temperature:.2f}"}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
self.pending_task_params.append(params)
|
||||
print(f"成功添加液体进料溶剂参数: material={assign_material_name}->ID:{material_id}, volume={volume}μL")
|
||||
print(f"当前队列长度: {len(self.pending_task_params)}")
|
||||
return json.dumps({"suc": True})
|
||||
|
||||
def liquid_feeding_titration(self, volume_formula: str, assign_material_name: str, titration_type: str = "1",
|
||||
time: str = "90", torque_variation: int = 2, temperature: float = 25.00):
|
||||
"""液体进料(滴定)"""
|
||||
self.append_to_workflow_sequence('{"web_workflow_name": "Liquid_feeding(titration)"}')
|
||||
material_id = self._get_material_id_by_name(assign_material_name)
|
||||
|
||||
if isinstance(temperature, str):
|
||||
temperature = float(temperature)
|
||||
|
||||
liquid_id = WORKFLOW_STEP_IDS["liquid_feeding_titration"]["liquid"]
|
||||
observe_id = WORKFLOW_STEP_IDS["liquid_feeding_titration"]["observe"]
|
||||
|
||||
params = {
|
||||
"param_values": {
|
||||
liquid_id: [
|
||||
{"m": 0, "n": 3, "Key": "volumeFormula", "Value": volume_formula},
|
||||
{"m": 0, "n": 3, "Key": "titrationType", "Value": titration_type},
|
||||
{"m": 0, "n": 3, "Key": "assignMaterialName", "Value": material_id}
|
||||
],
|
||||
observe_id: [
|
||||
{"m": 1, "n": 0, "Key": "time", "Value": time},
|
||||
{"m": 1, "n": 0, "Key": "torqueVariation", "Value": str(torque_variation)},
|
||||
{"m": 1, "n": 0, "Key": "temperature", "Value": f"{temperature:.2f}"}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
self.pending_task_params.append(params)
|
||||
print(f"成功添加液体进料滴定参数: volume={volume_formula}μL, material={assign_material_name}->ID:{material_id}")
|
||||
print(f"当前队列长度: {len(self.pending_task_params)}")
|
||||
return json.dumps({"suc": True})
|
||||
|
||||
def liquid_feeding_beaker(self, volume: str = "35000", assign_material_name: str = "BAPP",
|
||||
time: str = "0", torque_variation: str = "1", titrationType: str = "1",
|
||||
temperature: float = 25.00):
|
||||
"""液体进料烧杯"""
|
||||
self.append_to_workflow_sequence('{"web_workflow_name": "liquid_feeding_beaker"}')
|
||||
material_id = self._get_material_id_by_name(assign_material_name)
|
||||
|
||||
if isinstance(temperature, str):
|
||||
temperature = float(temperature)
|
||||
|
||||
liquid_id = WORKFLOW_STEP_IDS["liquid_feeding_beaker"]["liquid"]
|
||||
observe_id = WORKFLOW_STEP_IDS["liquid_feeding_beaker"]["observe"]
|
||||
|
||||
params = {
|
||||
"param_values": {
|
||||
liquid_id: [
|
||||
{"m": 0, "n": 2, "Key": "volume", "Value": volume},
|
||||
{"m": 0, "n": 2, "Key": "assignMaterialName", "Value": material_id},
|
||||
{"m": 0, "n": 2, "Key": "titrationType", "Value": titrationType}
|
||||
],
|
||||
observe_id: [
|
||||
{"m": 1, "n": 0, "Key": "time", "Value": time},
|
||||
{"m": 1, "n": 0, "Key": "torqueVariation", "Value": torque_variation},
|
||||
{"m": 1, "n": 0, "Key": "temperature", "Value": f"{temperature:.2f}"}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
self.pending_task_params.append(params)
|
||||
print(f"成功添加液体进料烧杯参数: volume={volume}μL, material={assign_material_name}->ID:{material_id}")
|
||||
print(f"当前队列长度: {len(self.pending_task_params)}")
|
||||
return json.dumps({"suc": True})
|
||||
File diff suppressed because it is too large
Load Diff
241
unilabos/registry/devices/bioyond.yaml
Normal file
241
unilabos/registry/devices/bioyond.yaml
Normal file
@@ -0,0 +1,241 @@
|
||||
workstation.bioyond_dispensing_station:
|
||||
category:
|
||||
- workstation
|
||||
- bioyond
|
||||
class:
|
||||
action_value_mappings:
|
||||
create_90_10_vial_feeding_task:
|
||||
feedback: {}
|
||||
goal:
|
||||
delay_time: delay_time
|
||||
hold_m_name: hold_m_name
|
||||
order_name: order_name
|
||||
percent_10_1_assign_material_name: percent_10_1_assign_material_name
|
||||
percent_10_1_liquid_material_name: percent_10_1_liquid_material_name
|
||||
percent_10_1_target_weigh: percent_10_1_target_weigh
|
||||
percent_10_1_volume: percent_10_1_volume
|
||||
percent_10_2_assign_material_name: percent_10_2_assign_material_name
|
||||
percent_10_2_liquid_material_name: percent_10_2_liquid_material_name
|
||||
percent_10_2_target_weigh: percent_10_2_target_weigh
|
||||
percent_10_2_volume: percent_10_2_volume
|
||||
percent_10_3_assign_material_name: percent_10_3_assign_material_name
|
||||
percent_10_3_liquid_material_name: percent_10_3_liquid_material_name
|
||||
percent_10_3_target_weigh: percent_10_3_target_weigh
|
||||
percent_10_3_volume: percent_10_3_volume
|
||||
percent_90_1_assign_material_name: percent_90_1_assign_material_name
|
||||
percent_90_1_target_weigh: percent_90_1_target_weigh
|
||||
percent_90_2_assign_material_name: percent_90_2_assign_material_name
|
||||
percent_90_2_target_weigh: percent_90_2_target_weigh
|
||||
percent_90_3_assign_material_name: percent_90_3_assign_material_name
|
||||
percent_90_3_target_weigh: percent_90_3_target_weigh
|
||||
speed: speed
|
||||
temperature: temperature
|
||||
goal_default:
|
||||
delay_time: ''
|
||||
hold_m_name: ''
|
||||
order_name: ''
|
||||
percent_10_1_assign_material_name: ''
|
||||
percent_10_1_liquid_material_name: ''
|
||||
percent_10_1_target_weigh: ''
|
||||
percent_10_1_volume: ''
|
||||
percent_10_2_assign_material_name: ''
|
||||
percent_10_2_liquid_material_name: ''
|
||||
percent_10_2_target_weigh: ''
|
||||
percent_10_2_volume: ''
|
||||
percent_10_3_assign_material_name: ''
|
||||
percent_10_3_liquid_material_name: ''
|
||||
percent_10_3_target_weigh: ''
|
||||
percent_10_3_volume: ''
|
||||
percent_90_1_assign_material_name: ''
|
||||
percent_90_1_target_weigh: ''
|
||||
percent_90_2_assign_material_name: ''
|
||||
percent_90_2_target_weigh: ''
|
||||
percent_90_3_assign_material_name: ''
|
||||
percent_90_3_target_weigh: ''
|
||||
speed: ''
|
||||
temperature: ''
|
||||
handles: []
|
||||
result:
|
||||
return_info: return_info
|
||||
schema:
|
||||
description: ''
|
||||
properties:
|
||||
feedback:
|
||||
properties: {}
|
||||
required: []
|
||||
title: DispenStationVialFeed_Feedback
|
||||
type: object
|
||||
goal:
|
||||
properties:
|
||||
delay_time:
|
||||
type: string
|
||||
hold_m_name:
|
||||
type: string
|
||||
order_name:
|
||||
type: string
|
||||
percent_10_1_assign_material_name:
|
||||
type: string
|
||||
percent_10_1_liquid_material_name:
|
||||
type: string
|
||||
percent_10_1_target_weigh:
|
||||
type: string
|
||||
percent_10_1_volume:
|
||||
type: string
|
||||
percent_10_2_assign_material_name:
|
||||
type: string
|
||||
percent_10_2_liquid_material_name:
|
||||
type: string
|
||||
percent_10_2_target_weigh:
|
||||
type: string
|
||||
percent_10_2_volume:
|
||||
type: string
|
||||
percent_10_3_assign_material_name:
|
||||
type: string
|
||||
percent_10_3_liquid_material_name:
|
||||
type: string
|
||||
percent_10_3_target_weigh:
|
||||
type: string
|
||||
percent_10_3_volume:
|
||||
type: string
|
||||
percent_90_1_assign_material_name:
|
||||
type: string
|
||||
percent_90_1_target_weigh:
|
||||
type: string
|
||||
percent_90_2_assign_material_name:
|
||||
type: string
|
||||
percent_90_2_target_weigh:
|
||||
type: string
|
||||
percent_90_3_assign_material_name:
|
||||
type: string
|
||||
percent_90_3_target_weigh:
|
||||
type: string
|
||||
speed:
|
||||
type: string
|
||||
temperature:
|
||||
type: string
|
||||
required:
|
||||
- order_name
|
||||
- percent_90_1_assign_material_name
|
||||
- percent_90_1_target_weigh
|
||||
- percent_90_2_assign_material_name
|
||||
- percent_90_2_target_weigh
|
||||
- percent_90_3_assign_material_name
|
||||
- percent_90_3_target_weigh
|
||||
- percent_10_1_assign_material_name
|
||||
- percent_10_1_target_weigh
|
||||
- percent_10_1_volume
|
||||
- percent_10_1_liquid_material_name
|
||||
- percent_10_2_assign_material_name
|
||||
- percent_10_2_target_weigh
|
||||
- percent_10_2_volume
|
||||
- percent_10_2_liquid_material_name
|
||||
- percent_10_3_assign_material_name
|
||||
- percent_10_3_target_weigh
|
||||
- percent_10_3_volume
|
||||
- percent_10_3_liquid_material_name
|
||||
- speed
|
||||
- temperature
|
||||
- delay_time
|
||||
- hold_m_name
|
||||
title: DispenStationVialFeed_Goal
|
||||
type: object
|
||||
result:
|
||||
properties:
|
||||
return_info:
|
||||
type: string
|
||||
required:
|
||||
- return_info
|
||||
title: DispenStationVialFeed_Result
|
||||
type: object
|
||||
required:
|
||||
- goal
|
||||
title: DispenStationVialFeed
|
||||
type: object
|
||||
type: DispenStationVialFeed
|
||||
create_diamine_solution_task:
|
||||
feedback: {}
|
||||
goal:
|
||||
delay_time: delay_time
|
||||
hold_m_name: hold_m_name
|
||||
liquid_material_name: liquid_material_name
|
||||
material_name: material_name
|
||||
order_name: order_name
|
||||
speed: speed
|
||||
target_weigh: target_weigh
|
||||
temperature: temperature
|
||||
volume: volume
|
||||
goal_default:
|
||||
delay_time: ''
|
||||
hold_m_name: ''
|
||||
liquid_material_name: ''
|
||||
material_name: ''
|
||||
order_name: ''
|
||||
speed: ''
|
||||
target_weigh: ''
|
||||
temperature: ''
|
||||
volume: ''
|
||||
handles: []
|
||||
result:
|
||||
return_info: return_info
|
||||
schema:
|
||||
description: ''
|
||||
properties:
|
||||
feedback:
|
||||
properties: {}
|
||||
required: []
|
||||
title: DispenStationSolnPrep_Feedback
|
||||
type: object
|
||||
goal:
|
||||
properties:
|
||||
delay_time:
|
||||
type: string
|
||||
hold_m_name:
|
||||
type: string
|
||||
liquid_material_name:
|
||||
type: string
|
||||
material_name:
|
||||
type: string
|
||||
order_name:
|
||||
type: string
|
||||
speed:
|
||||
type: string
|
||||
target_weigh:
|
||||
type: string
|
||||
temperature:
|
||||
type: string
|
||||
volume:
|
||||
type: string
|
||||
required:
|
||||
- order_name
|
||||
- material_name
|
||||
- target_weigh
|
||||
- volume
|
||||
- liquid_material_name
|
||||
- speed
|
||||
- temperature
|
||||
- delay_time
|
||||
- hold_m_name
|
||||
title: DispenStationSolnPrep_Goal
|
||||
type: object
|
||||
result:
|
||||
properties:
|
||||
return_info:
|
||||
type: string
|
||||
required:
|
||||
- return_info
|
||||
title: DispenStationSolnPrep_Result
|
||||
type: object
|
||||
required:
|
||||
- goal
|
||||
title: DispenStationSolnPrep
|
||||
type: object
|
||||
type: DispenStationSolnPrep
|
||||
module: unilabos.devices.workstation.bioyond_studio.dispensing_station:BioyondDispendsingStation
|
||||
status_types: {}
|
||||
type: python
|
||||
config_info: []
|
||||
description: ''
|
||||
handles: []
|
||||
icon: ''
|
||||
init_param_schema: {}
|
||||
version: 1.0.0
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,12 +2,13 @@ import importlib
|
||||
import inspect
|
||||
import json
|
||||
import traceback
|
||||
from typing import Union, Any, Dict, List
|
||||
from typing import Union, Any, Dict, List, Tuple
|
||||
import networkx as nx
|
||||
from pylabrobot.resources import ResourceHolder
|
||||
from unilabos_msgs.msg import Resource
|
||||
|
||||
from unilabos.resources.container import RegularContainer
|
||||
from unilabos.resources.itemized_carrier import ItemizedCarrier
|
||||
from unilabos.ros.msgs.message_converter import convert_to_ros_msg
|
||||
from unilabos.ros.nodes.resource_tracker import (
|
||||
ResourceDictInstance,
|
||||
@@ -576,13 +577,13 @@ def resource_plr_to_ulab(resource_plr: "ResourcePLR", parent_name: str = None, w
|
||||
return r
|
||||
|
||||
|
||||
def resource_bioyond_to_plr(bioyond_materials: list[dict], type_mapping: dict = {}, deck: Any = None) -> list[dict]:
|
||||
def resource_bioyond_to_plr(bioyond_materials: list[dict], type_mapping: Dict[str, Tuple[str, str]] = {}, deck: Any = None) -> list[dict]:
|
||||
"""
|
||||
将 bioyond 物料格式转换为 ulab 物料格式
|
||||
|
||||
Args:
|
||||
bioyond_materials: bioyond 系统的物料查询结果列表
|
||||
type_mapping: 物料类型映射字典,格式 {bioyond_type: plr_class_name}
|
||||
type_mapping: 物料类型映射字典,格式 {bioyond_type: [plr_class_name, class_uuid]}
|
||||
location_id_mapping: 库位 ID 到名称的映射字典,格式 {location_id: location_name}
|
||||
|
||||
Returns:
|
||||
@@ -592,7 +593,7 @@ def resource_bioyond_to_plr(bioyond_materials: list[dict], type_mapping: dict =
|
||||
|
||||
for material in bioyond_materials:
|
||||
className = (
|
||||
type_mapping.get(material.get("typeName"), "RegularContainer") if type_mapping else "RegularContainer"
|
||||
type_mapping.get(material.get("typeName"), ("RegularContainer", ""))[0] if type_mapping else "RegularContainer"
|
||||
)
|
||||
|
||||
plr_material: ResourcePLR = initialize_resource(
|
||||
@@ -614,7 +615,7 @@ def resource_bioyond_to_plr(bioyond_materials: list[dict], type_mapping: dict =
|
||||
# plr_material.unassign_child_resource(bottle)
|
||||
plr_material.sites[number] = None
|
||||
plr_material[number] = initialize_resource(
|
||||
{"name": f'{detail["name"]}_{number}', "class": type_mapping[detail["name"]]}, resource_type=ResourcePLR
|
||||
{"name": f'{detail["name"]}_{number}', "class": type_mapping[detail["name"]][0]}, resource_type=ResourcePLR
|
||||
)
|
||||
else:
|
||||
bottle.tracker.liquids = [
|
||||
@@ -645,32 +646,59 @@ def resource_bioyond_to_plr(bioyond_materials: list[dict], type_mapping: dict =
|
||||
return plr_materials
|
||||
|
||||
|
||||
def resource_plr_to_bioyond(plr_materials: list[ResourcePLR], type_mapping: dict = {}, warehouse_mapping: dict = {}) -> list[dict]:
|
||||
def resource_plr_to_bioyond(plr_resources: list[ResourcePLR], type_mapping: dict = {}, warehouse_mapping: dict = {}) -> list[dict]:
|
||||
bioyond_materials = []
|
||||
for plr_material in plr_materials:
|
||||
material = {
|
||||
"name": plr_material.name,
|
||||
"typeName": plr_material.__class__.__name__,
|
||||
"code": plr_material.code,
|
||||
"quantity": 0,
|
||||
"detail": [],
|
||||
"locations": [],
|
||||
}
|
||||
if hasattr(plr_material, "capacity") and plr_material.capacity > 1:
|
||||
for idx in range(plr_material.capacity):
|
||||
bottle = plr_material[idx]
|
||||
detail = {
|
||||
"x": (idx // (plr_material.num_items_x * plr_material.num_items_y)) + 1,
|
||||
"y": ((idx % (plr_material.num_items_x * plr_material.num_items_y)) // plr_material.num_items_x) + 1,
|
||||
"z": (idx % plr_material.num_items_x) + 1,
|
||||
for resource in plr_resources:
|
||||
if hasattr(resource, "capacity") and resource.capacity > 1:
|
||||
material = {
|
||||
"typeId": type_mapping.get(resource.model)[1],
|
||||
"name": resource.name,
|
||||
"unit": "个",
|
||||
"quantity": 1,
|
||||
"details": [],
|
||||
"Parameters": "{}"
|
||||
}
|
||||
for bottle in resource.children:
|
||||
if isinstance(resource, ItemizedCarrier):
|
||||
site = resource.get_child_identifier(bottle)
|
||||
else:
|
||||
site = {"x": bottle.location.x - 1, "y": bottle.location.y - 1}
|
||||
detail_item = {
|
||||
"typeId": type_mapping.get(bottle.model)[1],
|
||||
"name": bottle.name,
|
||||
"code": bottle.code if hasattr(bottle, "code") else "",
|
||||
"quantity": sum(qty for _, qty in bottle.tracker.liquids) if hasattr(bottle, "tracker") else 0,
|
||||
"x": site["x"] + 1,
|
||||
"y": site["y"] + 1,
|
||||
"molecular": 1,
|
||||
"Parameters": json.dumps({"molecular": 1})
|
||||
}
|
||||
material["detail"].append(detail)
|
||||
material["quantity"] = 1.0
|
||||
material["details"].append(detail_item)
|
||||
else:
|
||||
bottle = plr_material[0] if plr_material.capacity > 0 else plr_material
|
||||
material["quantity"] = sum(qty for _, qty in bottle.tracker.liquids) if hasattr(plr_material, "tracker") else 0
|
||||
bottle = resource[0] if resource.capacity > 0 else resource
|
||||
material = {
|
||||
"typeId": "3a14196b-24f2-ca49-9081-0cab8021bf1a",
|
||||
"name": resource.get("name", ""),
|
||||
"unit": "",
|
||||
"quantity": sum(qty for _, qty in bottle.tracker.liquids) if hasattr(bottle, "tracker") else 0,
|
||||
"Parameters": "{}"
|
||||
}
|
||||
|
||||
if resource.parent is not None and isinstance(resource.parent, ItemizedCarrier):
|
||||
site_in_parent = resource.parent.get_child_identifier(resource)
|
||||
material["locations"] = [
|
||||
{
|
||||
"id": warehouse_mapping[resource.parent.name]["site_uuids"][site_in_parent["identifier"]],
|
||||
"whid": warehouse_mapping[resource.parent.name]["uuid"],
|
||||
"whName": resource.parent.name,
|
||||
"x": site_in_parent["z"] + 1,
|
||||
"y": site_in_parent["y"] + 1,
|
||||
"z": 1,
|
||||
"quantity": 0
|
||||
}
|
||||
],
|
||||
|
||||
print(f"material_data: {material}")
|
||||
bioyond_materials.append(material)
|
||||
return bioyond_materials
|
||||
|
||||
|
||||
@@ -163,6 +163,89 @@ class ItemizedCarrier(ResourcePLR):
|
||||
if hasattr(resource, "unassign"):
|
||||
resource.unassign()
|
||||
|
||||
def get_child_identifier(self, child: ResourcePLR):
|
||||
"""Get the identifier information for a given child resource.
|
||||
|
||||
Args:
|
||||
child: The Resource object to find the identifier for
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing:
|
||||
- identifier: The string identifier (e.g. "A1", "B2")
|
||||
- idx: The integer index in the sites list
|
||||
- x: The x index (column index, 0-based)
|
||||
- y: The y index (row index, 0-based)
|
||||
- z: The z index (layer index, 0-based)
|
||||
|
||||
Raises:
|
||||
ValueError: If the child resource is not found in this carrier
|
||||
"""
|
||||
# Find the child resource in sites
|
||||
for idx, resource in enumerate(self.sites):
|
||||
if resource is child:
|
||||
# Get the identifier from ordering keys
|
||||
identifier = list(self._ordering.keys())[idx]
|
||||
|
||||
# Parse identifier to get x, y, z indices
|
||||
x_idx, y_idx, z_idx = self._parse_identifier_to_indices(identifier, idx)
|
||||
|
||||
return {
|
||||
"identifier": identifier,
|
||||
"idx": idx,
|
||||
"x": x_idx,
|
||||
"y": y_idx,
|
||||
"z": z_idx
|
||||
}
|
||||
|
||||
# If not found, raise an error
|
||||
raise ValueError(f"Resource {child} is not assigned to this carrier")
|
||||
|
||||
def _parse_identifier_to_indices(self, identifier: str, idx: int) -> Tuple[int, int, int]:
|
||||
"""Parse identifier string to get x, y, z indices.
|
||||
|
||||
Args:
|
||||
identifier: String identifier like "A1", "B2", etc.
|
||||
idx: Linear index as fallback for calculation
|
||||
|
||||
Returns:
|
||||
Tuple of (x_idx, y_idx, z_idx)
|
||||
"""
|
||||
# If we have explicit dimensions, calculate from idx
|
||||
if self.num_items_x > 0 and self.num_items_y > 0:
|
||||
# Calculate 3D indices from linear index
|
||||
z_idx = idx // (self.num_items_x * self.num_items_y) if self.num_items_z > 0 else 0
|
||||
remaining = idx % (self.num_items_x * self.num_items_y)
|
||||
y_idx = remaining // self.num_items_x
|
||||
x_idx = remaining % self.num_items_x
|
||||
return x_idx, y_idx, z_idx
|
||||
|
||||
# Fallback: parse from Excel-style identifier
|
||||
if isinstance(identifier, str) and len(identifier) >= 2:
|
||||
# Extract row (letter) and column (number)
|
||||
row_letters = ""
|
||||
col_numbers = ""
|
||||
|
||||
for char in identifier:
|
||||
if char.isalpha():
|
||||
row_letters += char
|
||||
elif char.isdigit():
|
||||
col_numbers += char
|
||||
|
||||
if row_letters and col_numbers:
|
||||
# Convert letter(s) to row index (A=0, B=1, etc.)
|
||||
y_idx = 0
|
||||
for char in row_letters:
|
||||
y_idx = y_idx * 26 + (ord(char.upper()) - ord('A'))
|
||||
|
||||
# Convert number to column index (1-based to 0-based)
|
||||
x_idx = int(col_numbers) - 1
|
||||
z_idx = 0 # Default layer
|
||||
|
||||
return x_idx, y_idx, z_idx
|
||||
|
||||
# If all else fails, assume linear arrangement
|
||||
return idx, 0, 0
|
||||
|
||||
def __getitem__(
|
||||
self,
|
||||
identifier: Union[str, int, Sequence[int], Sequence[str], slice, range],
|
||||
|
||||
Reference in New Issue
Block a user