mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-04 21:35:09 +00:00
Add post process station and related resources (#195)
* Add post process station and related resources - Created JSON configuration for post_process_station and its child post_process_deck. - Added YAML definitions for post_process_station, bottle carriers, bottles, and deck resources. - Implemented Python classes for bottle carriers, bottles, decks, and warehouses to manage resources in the post process. - Established a factory method for creating warehouses with customizable dimensions and layouts. - Defined the structure and behavior of the post_process_deck and its associated warehouses. * feat(post_process): add post_process_station and related warehouse functionality - Introduced post_process_station.json to define the post-processing station structure. - Implemented post_process_warehouse.py to create warehouse configurations with customizable layouts. - Added warehouses.py for specific warehouse configurations (4x3x1). - Updated post_process_station.yaml to reflect new module paths for OpcUaClient. - Refactored bottle carriers and bottles YAML files to point to the new module paths. - Adjusted deck.yaml to align with the new organizational structure for post_process_deck.
This commit is contained in:
93
unilabos/devices/workstation/post_process/bottle_carriers.py
Normal file
93
unilabos/devices/workstation/post_process/bottle_carriers.py
Normal file
@@ -0,0 +1,93 @@
|
||||
from pylabrobot.resources import create_homogeneous_resources, Coordinate, ResourceHolder, create_ordered_items_2d
|
||||
|
||||
from unilabos.resources.itemized_carrier import BottleCarrier
|
||||
from unilabos.devices.workstation.post_process.bottles import POST_PROCESS_PolymerStation_Reagent_Bottle
|
||||
|
||||
# 命名约定:试剂瓶-Bottle,烧杯-Beaker,烧瓶-Flask,小瓶-Vial
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 聚合站(PolymerStation)载体定义(统一入口)
|
||||
# ============================================================================
|
||||
|
||||
def POST_PROCESS_Raw_1BottleCarrier(name: str) -> BottleCarrier:
|
||||
"""聚合站-单试剂瓶载架
|
||||
|
||||
参数:
|
||||
- name: 载架名称前缀
|
||||
"""
|
||||
|
||||
# 载架尺寸 (mm)
|
||||
carrier_size_x = 127.8
|
||||
carrier_size_y = 85.5
|
||||
carrier_size_z = 20.0
|
||||
|
||||
# 烧杯/试剂瓶占位尺寸(使用圆形占位)
|
||||
beaker_diameter = 60.0
|
||||
|
||||
# 计算中央位置
|
||||
center_x = (carrier_size_x - beaker_diameter) / 2
|
||||
center_y = (carrier_size_y - beaker_diameter) / 2
|
||||
center_z = 5.0
|
||||
|
||||
carrier = BottleCarrier(
|
||||
name=name,
|
||||
size_x=carrier_size_x,
|
||||
size_y=carrier_size_y,
|
||||
size_z=carrier_size_z,
|
||||
sites=create_homogeneous_resources(
|
||||
klass=ResourceHolder,
|
||||
locations=[Coordinate(center_x, center_y, center_z)],
|
||||
resource_size_x=beaker_diameter,
|
||||
resource_size_y=beaker_diameter,
|
||||
name_prefix=name,
|
||||
),
|
||||
model="POST_PROCESS_Raw_1BottleCarrier",
|
||||
)
|
||||
carrier.num_items_x = 1
|
||||
carrier.num_items_y = 1
|
||||
carrier.num_items_z = 1
|
||||
# 统一后缀采用 "flask_1" 命名(可按需调整)
|
||||
carrier[0] = POST_PROCESS_PolymerStation_Reagent_Bottle(f"{name}_flask_1")
|
||||
return carrier
|
||||
|
||||
def POST_PROCESS_Reaction_1BottleCarrier(name: str) -> BottleCarrier:
|
||||
"""聚合站-单试剂瓶载架
|
||||
|
||||
参数:
|
||||
- name: 载架名称前缀
|
||||
"""
|
||||
|
||||
# 载架尺寸 (mm)
|
||||
carrier_size_x = 127.8
|
||||
carrier_size_y = 85.5
|
||||
carrier_size_z = 20.0
|
||||
|
||||
# 烧杯/试剂瓶占位尺寸(使用圆形占位)
|
||||
beaker_diameter = 60.0
|
||||
|
||||
# 计算中央位置
|
||||
center_x = (carrier_size_x - beaker_diameter) / 2
|
||||
center_y = (carrier_size_y - beaker_diameter) / 2
|
||||
center_z = 5.0
|
||||
|
||||
carrier = BottleCarrier(
|
||||
name=name,
|
||||
size_x=carrier_size_x,
|
||||
size_y=carrier_size_y,
|
||||
size_z=carrier_size_z,
|
||||
sites=create_homogeneous_resources(
|
||||
klass=ResourceHolder,
|
||||
locations=[Coordinate(center_x, center_y, center_z)],
|
||||
resource_size_x=beaker_diameter,
|
||||
resource_size_y=beaker_diameter,
|
||||
name_prefix=name,
|
||||
),
|
||||
model="POST_PROCESS_Reaction_1BottleCarrier",
|
||||
)
|
||||
carrier.num_items_x = 1
|
||||
carrier.num_items_y = 1
|
||||
carrier.num_items_z = 1
|
||||
# 统一后缀采用 "flask_1" 命名(可按需调整)
|
||||
carrier[0] = POST_PROCESS_PolymerStation_Reagent_Bottle(f"{name}_flask_1")
|
||||
return carrier
|
||||
20
unilabos/devices/workstation/post_process/bottles.py
Normal file
20
unilabos/devices/workstation/post_process/bottles.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from unilabos.resources.itemized_carrier import Bottle
|
||||
|
||||
|
||||
def POST_PROCESS_PolymerStation_Reagent_Bottle(
|
||||
name: str,
|
||||
diameter: float = 70.0,
|
||||
height: float = 120.0,
|
||||
max_volume: float = 500000.0, # 500mL
|
||||
barcode: str = None,
|
||||
) -> Bottle:
|
||||
"""创建试剂瓶"""
|
||||
return Bottle(
|
||||
name=name,
|
||||
diameter=diameter,
|
||||
height=height,
|
||||
max_volume=max_volume,
|
||||
barcode=barcode,
|
||||
model="POST_PROCESS_PolymerStation_Reagent_Bottle",
|
||||
)
|
||||
|
||||
46
unilabos/devices/workstation/post_process/decks.py
Normal file
46
unilabos/devices/workstation/post_process/decks.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from os import name
|
||||
from pylabrobot.resources import Deck, Coordinate, Rotation
|
||||
|
||||
from unilabos.devices.workstation.post_process.warehouses import (
|
||||
post_process_warehouse_4x3x1,
|
||||
post_process_warehouse_4x3x1_2,
|
||||
)
|
||||
|
||||
|
||||
|
||||
class post_process_deck(Deck):
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "post_process_deck",
|
||||
size_x: float = 2000.0,
|
||||
size_y: float = 1000.0,
|
||||
size_z: float = 2670.0,
|
||||
category: str = "deck",
|
||||
setup: bool = True,
|
||||
) -> None:
|
||||
super().__init__(name=name, size_x=1700.0, size_y=1350.0, size_z=2670.0)
|
||||
if setup:
|
||||
self.setup()
|
||||
|
||||
def setup(self) -> None:
|
||||
# 添加仓库
|
||||
self.warehouses = {
|
||||
"原料罐堆栈": post_process_warehouse_4x3x1("原料罐堆栈"),
|
||||
"反应罐堆栈": post_process_warehouse_4x3x1_2("反应罐堆栈"),
|
||||
|
||||
}
|
||||
# warehouse 的位置
|
||||
self.warehouse_locations = {
|
||||
"原料罐堆栈": Coordinate(350.0, 55.0, 0.0),
|
||||
"反应罐堆栈": Coordinate(1000.0, 55.0, 0.0),
|
||||
|
||||
}
|
||||
|
||||
for warehouse_name, warehouse in self.warehouses.items():
|
||||
self.assign_child_resource(warehouse, location=self.warehouse_locations[warehouse_name])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
157
unilabos/devices/workstation/post_process/opcua_huairou.json
Normal file
157
unilabos/devices/workstation/post_process/opcua_huairou.json
Normal file
@@ -0,0 +1,157 @@
|
||||
{
|
||||
"register_node_list_from_csv_path": {
|
||||
"path": "opcua_nodes_huairou.csv"
|
||||
},
|
||||
"create_flow": [
|
||||
{
|
||||
"name": "trigger_grab_action",
|
||||
"description": "触发反应罐及原料罐抓取动作",
|
||||
"parameters": ["reaction_tank_number", "raw_tank_number"],
|
||||
"action": [
|
||||
{
|
||||
"init_function": {
|
||||
"func_name": "init_grab_params",
|
||||
"write_nodes": ["reaction_tank_number", "raw_tank_number"]
|
||||
},
|
||||
"start_function": {
|
||||
"func_name": "start_grab",
|
||||
"write_nodes": {"grab_trigger": true},
|
||||
"condition_nodes": ["grab_complete"],
|
||||
"stop_condition_expression": "grab_complete == True",
|
||||
"timeout_seconds": 999999.0
|
||||
},
|
||||
"stop_function": {
|
||||
"func_name": "stop_grab",
|
||||
"write_nodes": {"grab_trigger": false}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "trigger_post_processing",
|
||||
"description": "触发后处理动作",
|
||||
"parameters": ["atomization_fast_speed", "wash_slow_speed","injection_pump_suction_speed",
|
||||
"injection_pump_push_speed","raw_liquid_suction_count","first_wash_water_amount",
|
||||
"second_wash_water_amount","first_powder_mixing_time","second_powder_mixing_time",
|
||||
"first_powder_wash_count","second_powder_wash_count","initial_water_amount",
|
||||
"pre_filtration_mixing_time","atomization_pressure_kpa"],
|
||||
"action": [
|
||||
{
|
||||
"init_function": {
|
||||
"func_name": "init_post_processing_params",
|
||||
"write_nodes": ["atomization_fast_speed", "wash_slow_speed","injection_pump_suction_speed",
|
||||
"injection_pump_push_speed","raw_liquid_suction_count","first_wash_water_amount",
|
||||
"second_wash_water_amount","first_powder_mixing_time","second_powder_mixing_time",
|
||||
"first_powder_wash_count","second_powder_wash_count","initial_water_amount",
|
||||
"pre_filtration_mixing_time","atomization_pressure_kpa"]
|
||||
},
|
||||
"start_function": {
|
||||
"func_name": "start_post_processing",
|
||||
"write_nodes": {"post_process_trigger": true},
|
||||
"condition_nodes": ["post_process_complete"],
|
||||
"stop_condition_expression": "post_process_complete == True",
|
||||
"timeout_seconds": 999999.0
|
||||
},
|
||||
"stop_function": {
|
||||
"func_name": "stop_post_processing",
|
||||
"write_nodes": {"post_process_trigger": false}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "trigger_cleaning_action",
|
||||
"description": "触发清洗及管路吹气动作",
|
||||
"parameters": ["nmp_outer_wall_cleaning_injection", "nmp_outer_wall_cleaning_count","nmp_outer_wall_cleaning_wait_time",
|
||||
"nmp_outer_wall_cleaning_waste_time","nmp_inner_wall_cleaning_injection","nmp_inner_wall_cleaning_count",
|
||||
"nmp_pump_cleaning_suction_count",
|
||||
"nmp_inner_wall_cleaning_waste_time",
|
||||
"nmp_stirrer_cleaning_injection",
|
||||
"nmp_stirrer_cleaning_count",
|
||||
"nmp_stirrer_cleaning_wait_time",
|
||||
"nmp_stirrer_cleaning_waste_time",
|
||||
"water_outer_wall_cleaning_injection",
|
||||
"water_outer_wall_cleaning_count",
|
||||
"water_outer_wall_cleaning_wait_time",
|
||||
"water_outer_wall_cleaning_waste_time",
|
||||
"water_inner_wall_cleaning_injection",
|
||||
"water_inner_wall_cleaning_count",
|
||||
"water_pump_cleaning_suction_count",
|
||||
"water_inner_wall_cleaning_waste_time",
|
||||
"water_stirrer_cleaning_injection",
|
||||
"water_stirrer_cleaning_count",
|
||||
"water_stirrer_cleaning_wait_time",
|
||||
"water_stirrer_cleaning_waste_time",
|
||||
"acetone_outer_wall_cleaning_injection",
|
||||
"acetone_outer_wall_cleaning_count",
|
||||
"acetone_outer_wall_cleaning_wait_time",
|
||||
"acetone_outer_wall_cleaning_waste_time",
|
||||
"acetone_inner_wall_cleaning_injection",
|
||||
"acetone_inner_wall_cleaning_count",
|
||||
"acetone_pump_cleaning_suction_count",
|
||||
"acetone_inner_wall_cleaning_waste_time",
|
||||
"acetone_stirrer_cleaning_injection",
|
||||
"acetone_stirrer_cleaning_count",
|
||||
"acetone_stirrer_cleaning_wait_time",
|
||||
"acetone_stirrer_cleaning_waste_time",
|
||||
"pipe_blowing_time",
|
||||
"injection_pump_forward_empty_suction_count",
|
||||
"injection_pump_reverse_empty_suction_count",
|
||||
"filtration_liquid_selection"],
|
||||
"action": [
|
||||
{
|
||||
"init_function": {
|
||||
"func_name": "init_cleaning_params",
|
||||
"write_nodes": ["nmp_outer_wall_cleaning_injection", "nmp_outer_wall_cleaning_count","nmp_outer_wall_cleaning_wait_time",
|
||||
"nmp_outer_wall_cleaning_waste_time","nmp_inner_wall_cleaning_injection","nmp_inner_wall_cleaning_count",
|
||||
"nmp_pump_cleaning_suction_count",
|
||||
"nmp_inner_wall_cleaning_waste_time",
|
||||
"nmp_stirrer_cleaning_injection",
|
||||
"nmp_stirrer_cleaning_count",
|
||||
"nmp_stirrer_cleaning_wait_time",
|
||||
"nmp_stirrer_cleaning_waste_time",
|
||||
"water_outer_wall_cleaning_injection",
|
||||
"water_outer_wall_cleaning_count",
|
||||
"water_outer_wall_cleaning_wait_time",
|
||||
"water_outer_wall_cleaning_waste_time",
|
||||
"water_inner_wall_cleaning_injection",
|
||||
"water_inner_wall_cleaning_count",
|
||||
"water_pump_cleaning_suction_count",
|
||||
"water_inner_wall_cleaning_waste_time",
|
||||
"water_stirrer_cleaning_injection",
|
||||
"water_stirrer_cleaning_count",
|
||||
"water_stirrer_cleaning_wait_time",
|
||||
"water_stirrer_cleaning_waste_time",
|
||||
"acetone_outer_wall_cleaning_injection",
|
||||
"acetone_outer_wall_cleaning_count",
|
||||
"acetone_outer_wall_cleaning_wait_time",
|
||||
"acetone_outer_wall_cleaning_waste_time",
|
||||
"acetone_inner_wall_cleaning_injection",
|
||||
"acetone_inner_wall_cleaning_count",
|
||||
"acetone_pump_cleaning_suction_count",
|
||||
"acetone_inner_wall_cleaning_waste_time",
|
||||
"acetone_stirrer_cleaning_injection",
|
||||
"acetone_stirrer_cleaning_count",
|
||||
"acetone_stirrer_cleaning_wait_time",
|
||||
"acetone_stirrer_cleaning_waste_time",
|
||||
"pipe_blowing_time",
|
||||
"injection_pump_forward_empty_suction_count",
|
||||
"injection_pump_reverse_empty_suction_count",
|
||||
"filtration_liquid_selection"]
|
||||
},
|
||||
"start_function": {
|
||||
"func_name": "start_cleaning",
|
||||
"write_nodes": {"cleaning_and_pipe_blowing_trigger": true},
|
||||
"condition_nodes": ["cleaning_complete"],
|
||||
"stop_condition_expression": "cleaning_complete == True",
|
||||
"timeout_seconds": 999999.0
|
||||
},
|
||||
"stop_function": {
|
||||
"func_name": "stop_cleaning",
|
||||
"write_nodes": {"cleaning_and_pipe_blowing_trigger": false}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
Name,EnglishName,NodeType,DataType,NodeLanguage,NodeId
|
||||
原料罐号码,raw_tank_number,VARIABLE,INT16,Chinese,ns=4;s=OPC|原料罐号码
|
||||
反应罐号码,reaction_tank_number,VARIABLE,INT16,Chinese,ns=4;s=OPC|反应罐号码
|
||||
反应罐及原料罐抓取触发,grab_trigger,VARIABLE,BOOLEAN,Chinese,ns=4;s=OPC|反应罐及原料罐抓取触发
|
||||
后处理动作触发,post_process_trigger,VARIABLE,BOOLEAN,Chinese,ns=4;s=OPC|后处理动作触发
|
||||
搅拌桨雾化快速,atomization_fast_speed,VARIABLE,FLOAT,Chinese,ns=4;s=OPC|搅拌桨雾化快速
|
||||
搅拌桨洗涤慢速,wash_slow_speed,VARIABLE,FLOAT,Chinese,ns=4;s=OPC|搅拌桨洗涤慢速
|
||||
注射泵抽液速度,injection_pump_suction_speed,VARIABLE,INT16,Chinese,ns=4;s=OPC|注射泵抽液速度
|
||||
注射泵推液速度,injection_pump_push_speed,VARIABLE,INT16,Chinese,ns=4;s=OPC|注射泵推液速度
|
||||
抽原液次数,raw_liquid_suction_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|抽原液次数
|
||||
第1次洗涤加水量,first_wash_water_amount,VARIABLE,FLOAT,Chinese,ns=4;s=OPC|第1次洗涤加水量
|
||||
第2次洗涤加水量,second_wash_water_amount,VARIABLE,FLOAT,Chinese,ns=4;s=OPC|第2次洗涤加水量
|
||||
第1次粉末搅拌时间,first_powder_mixing_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|第1次粉末搅拌时间
|
||||
第2次粉末搅拌时间,second_powder_mixing_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|第2次粉末搅拌时间
|
||||
第1次粉末洗涤次数,first_powder_wash_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|第1次粉末洗涤次数
|
||||
第2次粉末洗涤次数,second_powder_wash_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|第2次粉末洗涤次数
|
||||
最开始加水量,initial_water_amount,VARIABLE,FLOAT,Chinese,ns=4;s=OPC|最开始加水量
|
||||
抽滤前搅拌时间,pre_filtration_mixing_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|抽滤前搅拌时间
|
||||
雾化压力Kpa,atomization_pressure_kpa,VARIABLE,INT16,Chinese,ns=4;s=OPC|雾化压力Kpa
|
||||
清洗及管路吹气触发,cleaning_and_pipe_blowing_trigger,VARIABLE,BOOLEAN,Chinese,ns=4;s=OPC|清洗及管路吹气触发
|
||||
废液桶满报警,waste_tank_full_alarm,VARIABLE,BOOLEAN,Chinese,ns=4;s=OPC|废液桶满报警
|
||||
清水桶空报警,water_tank_empty_alarm,VARIABLE,BOOLEAN,Chinese,ns=4;s=OPC|清水桶空报警
|
||||
NMP桶空报警,nmp_tank_empty_alarm,VARIABLE,BOOLEAN,Chinese,ns=4;s=OPC|NMP桶空报警
|
||||
丙酮桶空报警,acetone_tank_empty_alarm,VARIABLE,BOOLEAN,Chinese,ns=4;s=OPC|丙酮桶空报警
|
||||
门开报警,door_open_alarm,VARIABLE,BOOLEAN,Chinese,ns=4;s=OPC|门开报警
|
||||
反应罐及原料罐抓取完成PLCtoPC,grab_complete,VARIABLE,BOOLEAN,Chinese,ns=4;s=OPC|反应罐及原料罐抓取完成PLCtoPC
|
||||
后处理动作完成PLCtoPC,post_process_complete,VARIABLE,BOOLEAN,Chinese,ns=4;s=OPC|后处理动作完成PLCtoPC
|
||||
清洗及管路吹气完成PLCtoPC,cleaning_complete,VARIABLE,BOOLEAN,Chinese,ns=4;s=OPC|清洗及管路吹气完成PLCtoPC
|
||||
远程模式PLCtoPC,remote_mode,VARIABLE,BOOLEAN,Chinese,ns=4;s=OPC|远程模式PLCtoPC
|
||||
设备准备就绪PLCtoPC,device_ready,VARIABLE,BOOLEAN,Chinese,ns=4;s=OPC|设备准备就绪PLCtoPC
|
||||
NMP外壁清洗加注,nmp_outer_wall_cleaning_injection,VARIABLE,FLOAT,Chinese,ns=4;s=OPC|NMP外壁清洗加注
|
||||
NMP外壁清洗次数,nmp_outer_wall_cleaning_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|NMP外壁清洗次数
|
||||
NMP外壁清洗等待时间,nmp_outer_wall_cleaning_wait_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|NMP外壁清洗等待时间
|
||||
NMP外壁清洗抽废时间,nmp_outer_wall_cleaning_waste_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|NMP外壁清洗抽废时间
|
||||
NMP内壁清洗加注,nmp_inner_wall_cleaning_injection,VARIABLE,FLOAT,Chinese,ns=4;s=OPC|NMP内壁清洗加注
|
||||
NMP内壁清洗次数,nmp_inner_wall_cleaning_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|NMP内壁清洗次数
|
||||
NMP泵清洗抽次数,nmp_pump_cleaning_suction_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|NMP泵清洗抽次数
|
||||
NMP内壁清洗抽废时间,nmp_inner_wall_cleaning_waste_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|NMP内壁清洗抽废时间
|
||||
NMP搅拌桨清洗加注,nmp_stirrer_cleaning_injection,VARIABLE,FLOAT,Chinese,ns=4;s=OPC|NMP搅拌桨清洗加注
|
||||
NMP搅拌桨清洗次数,nmp_stirrer_cleaning_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|NMP搅拌桨清洗次数
|
||||
NMP搅拌桨清洗等待时间,nmp_stirrer_cleaning_wait_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|NMP搅拌桨清洗等待时间
|
||||
NMP搅拌桨清洗抽废时间,nmp_stirrer_cleaning_waste_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|NMP搅拌桨清洗抽废时间
|
||||
清水外壁清洗加注,water_outer_wall_cleaning_injection,VARIABLE,FLOAT,Chinese,ns=4;s=OPC|清水外壁清洗加注
|
||||
清水外壁清洗次数,water_outer_wall_cleaning_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|清水外壁清洗次数
|
||||
清水外壁清洗等待时间,water_outer_wall_cleaning_wait_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|清水外壁清洗等待时间
|
||||
清水外壁清洗抽废时间,water_outer_wall_cleaning_waste_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|清水外壁清洗抽废时间
|
||||
清水内壁清洗加注,water_inner_wall_cleaning_injection,VARIABLE,FLOAT,Chinese,ns=4;s=OPC|清水内壁清洗加注
|
||||
清水内壁清洗次数,water_inner_wall_cleaning_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|清水内壁清洗次数
|
||||
清水泵清洗抽次数,water_pump_cleaning_suction_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|清水泵清洗抽次数
|
||||
清水内壁清洗抽废时间,water_inner_wall_cleaning_waste_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|清水内壁清洗抽废时间
|
||||
清水搅拌桨清洗加注,water_stirrer_cleaning_injection,VARIABLE,FLOAT,Chinese,ns=4;s=OPC|清水搅拌桨清洗加注
|
||||
清水搅拌桨清洗次数,water_stirrer_cleaning_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|清水搅拌桨清洗次数
|
||||
清水搅拌桨清洗等待时间,water_stirrer_cleaning_wait_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|清水搅拌桨清洗等待时间
|
||||
清水搅拌桨清洗抽废时间,water_stirrer_cleaning_waste_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|清水搅拌桨清洗抽废时间
|
||||
丙酮外壁清洗加注,acetone_outer_wall_cleaning_injection,VARIABLE,FLOAT,Chinese,ns=4;s=OPC|丙酮外壁清洗加注
|
||||
丙酮外壁清洗次数,acetone_outer_wall_cleaning_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|丙酮外壁清洗次数
|
||||
丙酮外壁清洗等待时间,acetone_outer_wall_cleaning_wait_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|丙酮外壁清洗等待时间
|
||||
丙酮外壁清洗抽废时间,acetone_outer_wall_cleaning_waste_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|丙酮外壁清洗抽废时间
|
||||
丙酮内壁清洗加注,acetone_inner_wall_cleaning_injection,VARIABLE,FLOAT,Chinese,ns=4;s=OPC|丙酮内壁清洗加注
|
||||
丙酮内壁清洗次数,acetone_inner_wall_cleaning_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|丙酮内壁清洗次数
|
||||
丙酮泵清洗抽次数,acetone_pump_cleaning_suction_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|丙酮泵清洗抽次数
|
||||
丙酮内壁清洗抽废时间,acetone_inner_wall_cleaning_waste_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|丙酮内壁清洗抽废时间
|
||||
丙酮搅拌桨清洗加注,acetone_stirrer_cleaning_injection,VARIABLE,FLOAT,Chinese,ns=4;s=OPC|丙酮搅拌桨清洗加注
|
||||
丙酮搅拌桨清洗次数,acetone_stirrer_cleaning_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|丙酮搅拌桨清洗次数
|
||||
丙酮搅拌桨清洗等待时间,acetone_stirrer_cleaning_wait_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|丙酮搅拌桨清洗等待时间
|
||||
丙酮搅拌桨清洗抽废时间,acetone_stirrer_cleaning_waste_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|丙酮搅拌桨清洗抽废时间
|
||||
管道吹气时间,pipe_blowing_time,VARIABLE,INT32,Chinese,ns=4;s=OPC|管道吹气时间
|
||||
注射泵正向空抽次数,injection_pump_forward_empty_suction_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|注射泵正向空抽次数
|
||||
注射泵反向空抽次数,injection_pump_reverse_empty_suction_count,VARIABLE,INT16,Chinese,ns=4;s=OPC|注射泵反向空抽次数
|
||||
抽滤液选择0水1丙酮,filtration_liquid_selection,VARIABLE,INT16,Chinese,ns=4;s=OPC|抽滤液选择0水1丙酮
|
||||
|
1781
unilabos/devices/workstation/post_process/post_process.py
Normal file
1781
unilabos/devices/workstation/post_process/post_process.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "post_process_station",
|
||||
"name": "post_process_station",
|
||||
"children": [
|
||||
"post_process_deck"
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "post_process_station",
|
||||
"config": {
|
||||
"url": "opc.tcp://LAPTOP-AN6QGCSD:53530/OPCUA/SimulationServer",
|
||||
"config_path": "C:\\Users\\Roy\\Desktop\\DPLC\\Uni-Lab-OS\\unilabos\\devices\\workstation\\post_process\\opcua_huairou.json",
|
||||
"deck": {
|
||||
"data": {
|
||||
"_resource_child_name": "post_process_deck",
|
||||
"_resource_type": "unilabos.devices.workstation.post_process.decks:post_process_deck"
|
||||
}
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "post_process_deck",
|
||||
"name": "post_process_deck",
|
||||
"sample_id": null,
|
||||
"children": [],
|
||||
"parent": "post_process_station",
|
||||
"type": "deck",
|
||||
"class": "post_process_deck",
|
||||
"position": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"type": "post_process_deck",
|
||||
"setup": true
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
from typing import Dict, Optional, List, Union
|
||||
from pylabrobot.resources import Coordinate
|
||||
from pylabrobot.resources.carrier import ResourceHolder, create_homogeneous_resources
|
||||
|
||||
from unilabos.resources.itemized_carrier import ItemizedCarrier, ResourcePLR
|
||||
|
||||
|
||||
LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
|
||||
def warehouse_factory(
|
||||
name: str,
|
||||
num_items_x: int = 1,
|
||||
num_items_y: int = 4,
|
||||
num_items_z: int = 4,
|
||||
dx: float = 137.0,
|
||||
dy: float = 96.0,
|
||||
dz: float = 120.0,
|
||||
item_dx: float = 10.0,
|
||||
item_dy: float = 10.0,
|
||||
item_dz: float = 10.0,
|
||||
resource_size_x: float = 127.0,
|
||||
resource_size_y: float = 86.0,
|
||||
resource_size_z: float = 25.0,
|
||||
removed_positions: Optional[List[int]] = None,
|
||||
empty: bool = False,
|
||||
category: str = "warehouse",
|
||||
model: Optional[str] = None,
|
||||
col_offset: int = 0, # 列起始偏移量,用于生成5-8等命名
|
||||
layout: str = "col-major", # 新增:排序方式,"col-major"=列优先,"row-major"=行优先
|
||||
):
|
||||
# 创建位置坐标
|
||||
locations = []
|
||||
|
||||
for layer in range(num_items_z): # 层
|
||||
for row in range(num_items_y): # 行
|
||||
for col in range(num_items_x): # 列
|
||||
# 计算位置
|
||||
x = dx + col * item_dx
|
||||
|
||||
# 根据 layout 决定 y 坐标计算
|
||||
if layout == "row-major":
|
||||
# 行优先:row=0(第1行) 应该显示在上方,y 值最小
|
||||
y = dy + row * item_dy
|
||||
else:
|
||||
# 列优先:保持原逻辑
|
||||
y = dy + (num_items_y - row - 1) * item_dy
|
||||
|
||||
z = dz + (num_items_z - layer - 1) * item_dz
|
||||
locations.append(Coordinate(x, y, z))
|
||||
|
||||
if removed_positions:
|
||||
locations = [loc for i, loc in enumerate(locations) if i not in removed_positions]
|
||||
|
||||
_sites = create_homogeneous_resources(
|
||||
klass=ResourceHolder,
|
||||
locations=locations,
|
||||
resource_size_x=resource_size_x,
|
||||
resource_size_y=resource_size_y,
|
||||
resource_size_z=resource_size_z,
|
||||
name_prefix=name,
|
||||
)
|
||||
|
||||
len_x, len_y = (num_items_x, num_items_y) if num_items_z == 1 else (num_items_y, num_items_z) if num_items_x == 1 else (num_items_x, num_items_z)
|
||||
|
||||
# 🔑 修改:使用数字命名,最上面是4321,最下面是12,11,10,9
|
||||
# 命名顺序必须与坐标生成顺序一致:层 → 行 → 列
|
||||
keys = []
|
||||
for layer in range(num_items_z): # 遍历每一层
|
||||
for row in range(num_items_y): # 遍历每一行
|
||||
for col in range(num_items_x): # 遍历每一列
|
||||
# 倒序计算全局行号:row=0 应该对应 global_row=0(第1行:4321)
|
||||
# row=1 应该对应 global_row=1(第2行:8765)
|
||||
# row=2 应该对应 global_row=2(第3行:12,11,10,9)
|
||||
# 但前端显示时 row=2 在最上面,所以需要反转
|
||||
reversed_row = (num_items_y - 1 - row) # row=0→reversed_row=2, row=1→reversed_row=1, row=2→reversed_row=0
|
||||
global_row = layer * num_items_y + reversed_row
|
||||
|
||||
# 每行的最大数字 = (global_row + 1) * num_items_x + col_offset
|
||||
base_num = (global_row + 1) * num_items_x + col_offset
|
||||
|
||||
# 从右到左递减:4,3,2,1
|
||||
key = str(base_num - col)
|
||||
keys.append(key)
|
||||
|
||||
sites = {i: site for i, site in zip(keys, _sites.values())}
|
||||
|
||||
return WareHouse(
|
||||
name=name,
|
||||
size_x=dx + item_dx * num_items_x,
|
||||
size_y=dy + item_dy * num_items_y,
|
||||
size_z=dz + item_dz * num_items_z,
|
||||
num_items_x = num_items_x,
|
||||
num_items_y = num_items_y,
|
||||
num_items_z = num_items_z,
|
||||
ordering_layout=layout, # 传递排序方式到 ordering_layout
|
||||
sites=sites,
|
||||
category=category,
|
||||
model=model,
|
||||
)
|
||||
|
||||
|
||||
class WareHouse(ItemizedCarrier):
|
||||
"""堆栈载体类 - 可容纳16个板位的载体(4层x4行x1列)"""
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
size_x: float,
|
||||
size_y: float,
|
||||
size_z: float,
|
||||
num_items_x: int,
|
||||
num_items_y: int,
|
||||
num_items_z: int,
|
||||
layout: str = "x-y",
|
||||
sites: Optional[Dict[Union[int, str], Optional[ResourcePLR]]] = None,
|
||||
category: str = "warehouse",
|
||||
model: Optional[str] = None,
|
||||
ordering_layout: str = "col-major",
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(
|
||||
name=name,
|
||||
size_x=size_x,
|
||||
size_y=size_y,
|
||||
size_z=size_z,
|
||||
# ordered_items=ordered_items,
|
||||
# ordering=ordering,
|
||||
num_items_x=num_items_x,
|
||||
num_items_y=num_items_y,
|
||||
num_items_z=num_items_z,
|
||||
layout=layout,
|
||||
sites=sites,
|
||||
category=category,
|
||||
model=model,
|
||||
)
|
||||
|
||||
# 保存排序方式,供graphio.py的坐标映射使用
|
||||
# 使用独立属性避免与父类的layout冲突
|
||||
self.ordering_layout = ordering_layout
|
||||
|
||||
def serialize(self) -> dict:
|
||||
"""序列化时保存 ordering_layout 属性"""
|
||||
data = super().serialize()
|
||||
data['ordering_layout'] = self.ordering_layout
|
||||
return data
|
||||
|
||||
def get_site_by_layer_position(self, row: int, col: int, layer: int) -> ResourceHolder:
|
||||
if not (0 <= layer < 4 and 0 <= row < 4 and 0 <= col < 1):
|
||||
raise ValueError("无效的位置: layer={}, row={}, col={}".format(layer, row, col))
|
||||
|
||||
site_index = layer * 4 + row * 1 + col
|
||||
return self.sites[site_index]
|
||||
|
||||
def add_rack_to_position(self, row: int, col: int, layer: int, rack) -> None:
|
||||
site = self.get_site_by_layer_position(row, col, layer)
|
||||
site.assign_child_resource(rack)
|
||||
|
||||
def get_rack_at_position(self, row: int, col: int, layer: int):
|
||||
site = self.get_site_by_layer_position(row, col, layer)
|
||||
return site.resource
|
||||
38
unilabos/devices/workstation/post_process/warehouses.py
Normal file
38
unilabos/devices/workstation/post_process/warehouses.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from unilabos.devices.workstation.post_process.post_process_warehouse import WareHouse, warehouse_factory
|
||||
|
||||
|
||||
|
||||
# =================== Other ===================
|
||||
|
||||
|
||||
def post_process_warehouse_4x3x1(name: str) -> WareHouse:
|
||||
"""创建post_process 4x3x1仓库"""
|
||||
return warehouse_factory(
|
||||
name=name,
|
||||
num_items_x=4,
|
||||
num_items_y=3,
|
||||
num_items_z=1,
|
||||
dx=10.0,
|
||||
dy=10.0,
|
||||
dz=10.0,
|
||||
item_dx=137.0,
|
||||
item_dy=96.0,
|
||||
item_dz=120.0,
|
||||
category="warehouse",
|
||||
)
|
||||
|
||||
def post_process_warehouse_4x3x1_2(name: str) -> WareHouse:
|
||||
"""已弃用:创建post_process 4x3x1仓库"""
|
||||
return warehouse_factory(
|
||||
name=name,
|
||||
num_items_x=4,
|
||||
num_items_y=3,
|
||||
num_items_z=1,
|
||||
dx=12.0,
|
||||
dy=12.0,
|
||||
dz=12.0,
|
||||
item_dx=137.0,
|
||||
item_dy=96.0,
|
||||
item_dz=120.0,
|
||||
category="warehouse",
|
||||
)
|
||||
Reference in New Issue
Block a user