mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2025-12-17 13:01:12 +00:00
Merge branch 'main' into pr/39
This commit is contained in:
@@ -1,11 +1,96 @@
|
||||
liquid_handler:
|
||||
description: Liquid handler device controlled by pylabrobot
|
||||
class:
|
||||
module: pylabrobot.liquid_handling:LiquidHandler
|
||||
module: unilabos.devices.liquid_handling.liquid_handler_abstract:LiquidHandlerAbstract
|
||||
type: python
|
||||
status_types:
|
||||
name: String
|
||||
action_value_mappings:
|
||||
remove:
|
||||
type: LiquidHandlerRemove
|
||||
goal:
|
||||
vols: vols
|
||||
sources: sources
|
||||
waste_liquid: waste_liquid
|
||||
use_channels: use_channels
|
||||
flow_rates: flow_rates
|
||||
offsets: offsets
|
||||
liquid_height: liquid_height
|
||||
blow_out_air_volume: blow_out_air_volume
|
||||
spread: spread
|
||||
delays: delays
|
||||
is_96_well: is_96_well
|
||||
top: top
|
||||
none_keys: none_keys
|
||||
feedback: { }
|
||||
result: { }
|
||||
add_liquid:
|
||||
type: LiquidHandlerAdd
|
||||
goal:
|
||||
asp_vols: asp_vols
|
||||
dis_vols: dis_vols
|
||||
reagent_sources: reagent_sources
|
||||
targets: targets
|
||||
use_channels: use_channels
|
||||
flow_rates: flow_rates
|
||||
offsets: offsets
|
||||
liquid_height: liquid_height
|
||||
blow_out_air_volume: blow_out_air_volume
|
||||
spread: spread
|
||||
is_96_well: is_96_well
|
||||
mix_time: mix_time
|
||||
mix_vol: mix_vol
|
||||
mix_rate: mix_rate
|
||||
mix_liquid_height: mix_liquid_height
|
||||
none_keys: none_keys
|
||||
feedback: { }
|
||||
result: { }
|
||||
transfer_liquid:
|
||||
type: LiquidHandlerTransfer
|
||||
goal:
|
||||
asp_vols: asp_vols
|
||||
dis_vols: dis_vols
|
||||
sources: sources
|
||||
targets: targets
|
||||
tip_racks: tip_racks
|
||||
use_channels: use_channels
|
||||
asp_flow_rates: asp_flow_rates
|
||||
dis_flow_rates: dis_flow_rates
|
||||
offsets: offsets
|
||||
touch_tip: touch_tip
|
||||
liquid_height: liquid_height
|
||||
blow_out_air_volume: blow_out_air_volume
|
||||
spread: spread
|
||||
is_96_well: is_96_well
|
||||
mix_stage: mix_stage
|
||||
mix_times: mix_times
|
||||
mix_vol: mix_vol
|
||||
mix_rate: mix_rate
|
||||
mix_liquid_height: mix_liquid_height
|
||||
delays: delays
|
||||
none_keys: none_keys
|
||||
feedback: { }
|
||||
result: { }
|
||||
mix:
|
||||
type: LiquidHandlerMix
|
||||
goal:
|
||||
targets: targets
|
||||
mix_time: mix_time
|
||||
mix_vol: mix_vol
|
||||
height_to_bottom: height_to_bottom
|
||||
offsets: offsets
|
||||
mix_rate: mix_rate
|
||||
none_keys: none_keys
|
||||
feedback: { }
|
||||
result: { }
|
||||
move_to:
|
||||
type: LiquidHandlerMoveTo
|
||||
goal:
|
||||
well: well
|
||||
dis_to_top: dis_to_top
|
||||
channel: channel
|
||||
feedback: { }
|
||||
result: { }
|
||||
aspirate:
|
||||
type: LiquidHandlerAspirate
|
||||
goal:
|
||||
@@ -170,127 +255,6 @@ liquid_handler:
|
||||
- name
|
||||
additionalProperties: false
|
||||
|
||||
dp_liquid_handler:
|
||||
description: 通用液体处理
|
||||
class:
|
||||
module: unilabos.devices.liquid_handling.action_definition:DPLiquidHandler
|
||||
type: python
|
||||
status_types:
|
||||
status: String
|
||||
action_value_mappings:
|
||||
remove_liquid:
|
||||
type: DPLiquidHandlerRemoveLiquid
|
||||
goal:
|
||||
vols: vols
|
||||
sources: sources
|
||||
waste_liquid: waste_liquid
|
||||
use_channels: use_channels
|
||||
flow_rates: flow_rates
|
||||
offsets: offsets
|
||||
liquid_height: liquid_height
|
||||
blow_out_air_volume: blow_out_air_volume
|
||||
spread: spread
|
||||
delays: delays
|
||||
is_96_well: is_96_well
|
||||
top: top
|
||||
none_keys: none_keys
|
||||
feedback: {}
|
||||
result: {}
|
||||
add_liquid:
|
||||
type: DPLiquidHandlerAddLiquid
|
||||
goal:
|
||||
asp_vols: asp_vols
|
||||
dis_vols: dis_vols
|
||||
reagent_sources: reagent_sources
|
||||
targets: targets
|
||||
use_channels: use_channels
|
||||
flow_rates: flow_rates
|
||||
offsets: offsets
|
||||
liquid_height: liquid_height
|
||||
blow_out_air_volume: blow_out_air_volume
|
||||
spread: spread
|
||||
is_96_well: is_96_well
|
||||
mix_time: mix_time
|
||||
mix_vol: mix_vol
|
||||
mix_rate: mix_rate
|
||||
mix_liquid_height: mix_liquid_height
|
||||
none_keys: none_keys
|
||||
feedback: {}
|
||||
result: {}
|
||||
transfer_liquid:
|
||||
type: DPLiquidHandlerTransferLiquid
|
||||
goal:
|
||||
asp_vols: asp_vols
|
||||
dis_vols: dis_vols
|
||||
sources: sources
|
||||
targets: targets
|
||||
tip_racks: tip_racks
|
||||
use_channels: use_channels
|
||||
asp_flow_rates: asp_flow_rates
|
||||
dis_flow_rates: dis_flow_rates
|
||||
offsets: offsets
|
||||
touch_tip: touch_tip
|
||||
liquid_height: liquid_height
|
||||
blow_out_air_volume: blow_out_air_volume
|
||||
spread: spread
|
||||
is_96_well: is_96_well
|
||||
mix_stage: mix_stage
|
||||
mix_times: mix_times
|
||||
mix_vol: mix_vol
|
||||
mix_rate: mix_rate
|
||||
mix_liquid_height: mix_liquid_height
|
||||
delays: delays
|
||||
none_keys: none_keys
|
||||
feedback: {}
|
||||
result: {}
|
||||
custom_delay:
|
||||
type: DPLiquidHandlerCustomDelay
|
||||
goal:
|
||||
seconds: seconds
|
||||
msg: msg
|
||||
feedback: {}
|
||||
result: {}
|
||||
touch_tip:
|
||||
type: DPLiquidHandlerTouchTip
|
||||
goal:
|
||||
targets: targets
|
||||
feedback: {}
|
||||
result: {}
|
||||
mix:
|
||||
type: DPLiquidHandlerMix
|
||||
goal:
|
||||
targets: targets
|
||||
mix_time: mix_time
|
||||
mix_vol: mix_vol
|
||||
height_to_bottom: height_to_bottom
|
||||
offsets: offsets
|
||||
mix_rate: mix_rate
|
||||
none_keys: none_keys
|
||||
feedback: {}
|
||||
result: {}
|
||||
set_tiprack:
|
||||
type: DPLiquidHandlerSetTiprack
|
||||
goal:
|
||||
tip_racks: tip_racks
|
||||
feedback: {}
|
||||
result: {}
|
||||
move_to:
|
||||
type: DPLiquidHandlerMoveTo
|
||||
goal:
|
||||
well: well
|
||||
dis_to_top: dis_to_top
|
||||
channel: channel
|
||||
feedback: {}
|
||||
result: {}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: 物料名
|
||||
required:
|
||||
- name
|
||||
|
||||
liquid_handler.revvity:
|
||||
class:
|
||||
module: unilabos.devices.liquid_handling.revvity:Revvity
|
||||
|
||||
@@ -12,7 +12,7 @@ separator.homemade:
|
||||
goal:
|
||||
stir_time: stir_time,
|
||||
stir_speed: stir_speed
|
||||
settling_time": settling_time
|
||||
settling_time: settling_time
|
||||
feedback:
|
||||
status: status
|
||||
result:
|
||||
|
||||
@@ -3,6 +3,25 @@ vacuum_pump.mock:
|
||||
class:
|
||||
module: unilabos.devices.pump_and_valve.vacuum_pump_mock:VacuumPumpMock
|
||||
type: python
|
||||
status_types:
|
||||
status: String
|
||||
action_value_mappings:
|
||||
open:
|
||||
type: EmptyIn
|
||||
goal: {}
|
||||
feedback: {}
|
||||
result: {}
|
||||
close:
|
||||
type: EmptyIn
|
||||
goal: {}
|
||||
feedback: {}
|
||||
result: {}
|
||||
set_status:
|
||||
type: StrSingleInput
|
||||
goal:
|
||||
string: string
|
||||
feedback: {}
|
||||
result: {}
|
||||
|
||||
gas_source.mock:
|
||||
description: Mock gas source
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
@@ -7,10 +6,9 @@ from typing import Any
|
||||
|
||||
import yaml
|
||||
|
||||
from unilabos.utils import logger
|
||||
from unilabos.ros.msgs.message_converter import msg_converter_manager, ros_action_to_json_schema
|
||||
from unilabos.utils import logger
|
||||
from unilabos.utils.decorator import singleton
|
||||
from unilabos.utils.type_check import TypeEncoder
|
||||
|
||||
DEFAULT_PATHS = [Path(__file__).absolute().parent]
|
||||
|
||||
@@ -21,43 +19,16 @@ class Registry:
|
||||
self.registry_paths = DEFAULT_PATHS.copy() # 使用copy避免修改默认值
|
||||
if registry_paths:
|
||||
self.registry_paths.extend(registry_paths)
|
||||
action_type = self._replace_type_with_class(
|
||||
"ResourceCreateFromOuter", "host_node", f"动作 add_resource_from_outer"
|
||||
self.ResourceCreateFromOuter = self._replace_type_with_class(
|
||||
"ResourceCreateFromOuter", "host_node", f"动作 create_resource_detailed"
|
||||
)
|
||||
schema = ros_action_to_json_schema(action_type)
|
||||
self.device_type_registry = {
|
||||
"host_node": {
|
||||
"description": "UniLabOS主机节点",
|
||||
"class": {
|
||||
"module": "unilabos.ros.nodes.presets.host_node",
|
||||
"type": "python",
|
||||
"status_types": {},
|
||||
"action_value_mappings": {
|
||||
"add_resource_from_outer": {
|
||||
"type": msg_converter_manager.search_class("ResourceCreateFromOuter"),
|
||||
"goal": {
|
||||
"resources": "resources",
|
||||
"device_ids": "device_ids",
|
||||
"bind_parent_ids": "bind_parent_ids",
|
||||
"bind_locations": "bind_locations",
|
||||
"other_calling_params": "other_calling_params",
|
||||
},
|
||||
"feedback": {},
|
||||
"result": {
|
||||
"success": "success"
|
||||
},
|
||||
"schema": schema
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"properties": {},
|
||||
"additionalProperties": False,
|
||||
"type": "object"
|
||||
},
|
||||
"file_path": "/"
|
||||
}
|
||||
}
|
||||
self.ResourceCreateFromOuterEasy = self._replace_type_with_class(
|
||||
"ResourceCreateFromOuterEasy", "host_node", f"动作 create_resource"
|
||||
)
|
||||
self.EmptyIn = self._replace_type_with_class(
|
||||
"EmptyIn", "host_node", f""
|
||||
)
|
||||
self.device_type_registry = {}
|
||||
self.resource_type_registry = {}
|
||||
self._setup_called = False # 跟踪setup是否已调用
|
||||
# 其他状态变量
|
||||
@@ -69,9 +40,70 @@ class Registry:
|
||||
logger.critical("[UniLab Registry] setup方法已被调用过,不允许多次调用")
|
||||
return
|
||||
|
||||
# 标记setup已被调用
|
||||
self._setup_called = True
|
||||
from unilabos.app.web.utils.action_utils import get_yaml_from_goal_type
|
||||
|
||||
self.device_type_registry.update(
|
||||
{
|
||||
"host_node": {
|
||||
"description": "UniLabOS主机节点",
|
||||
"class": {
|
||||
"module": "unilabos.ros.nodes.presets.host_node",
|
||||
"type": "python",
|
||||
"status_types": {},
|
||||
"action_value_mappings": {
|
||||
"create_resource_detailed": {
|
||||
"type": self.ResourceCreateFromOuter,
|
||||
"goal": {
|
||||
"resources": "resources",
|
||||
"device_ids": "device_ids",
|
||||
"bind_parent_ids": "bind_parent_ids",
|
||||
"bind_locations": "bind_locations",
|
||||
"other_calling_params": "other_calling_params",
|
||||
},
|
||||
"feedback": {},
|
||||
"result": {"success": "success"},
|
||||
"schema": ros_action_to_json_schema(self.ResourceCreateFromOuter),
|
||||
"goal_default": yaml.safe_load(
|
||||
io.StringIO(get_yaml_from_goal_type(self.ResourceCreateFromOuter.Goal))
|
||||
),
|
||||
},
|
||||
"create_resource": {
|
||||
"type": self.ResourceCreateFromOuterEasy,
|
||||
"goal": {
|
||||
"res_id": "res_id",
|
||||
"class_name": "class_name",
|
||||
"parent": "parent",
|
||||
"device_id": "device_id",
|
||||
"bind_locations": "bind_locations",
|
||||
"liquid_input_slot": "liquid_input_slot[]",
|
||||
"liquid_type": "liquid_type[]",
|
||||
"liquid_volume": "liquid_volume[]",
|
||||
"slot_on_deck": "slot_on_deck",
|
||||
},
|
||||
"feedback": {},
|
||||
"result": {"success": "success"},
|
||||
"schema": ros_action_to_json_schema(self.ResourceCreateFromOuterEasy),
|
||||
"goal_default": yaml.safe_load(
|
||||
io.StringIO(get_yaml_from_goal_type(self.ResourceCreateFromOuterEasy.Goal))
|
||||
),
|
||||
},
|
||||
"test_latency": {
|
||||
"type": self.EmptyIn,
|
||||
"goal": {},
|
||||
"feedback": {},
|
||||
"result": {"latency_ms": "latency_ms", "time_diff_ms": "time_diff_ms"},
|
||||
"schema": ros_action_to_json_schema(self.EmptyIn),
|
||||
"goal_default": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
"icon": "icon_device.webp",
|
||||
"registry_type": "device",
|
||||
"schema": {"properties": {}, "additionalProperties": False, "type": "object"},
|
||||
"file_path": "/",
|
||||
}
|
||||
}
|
||||
)
|
||||
logger.debug(f"[UniLab Registry] ----------Setup----------")
|
||||
self.registry_paths = [Path(path).absolute() for path in self.registry_paths]
|
||||
for i, path in enumerate(self.registry_paths):
|
||||
@@ -81,6 +113,8 @@ class Registry:
|
||||
self.load_device_types(path)
|
||||
self.load_resource_types(path)
|
||||
logger.info("[UniLab Registry] 注册表设置完成")
|
||||
# 标记setup已被调用
|
||||
self._setup_called = True
|
||||
|
||||
def load_resource_types(self, path: os.PathLike):
|
||||
abs_path = Path(path).absolute()
|
||||
@@ -96,6 +130,9 @@ class Registry:
|
||||
resource_info["file_path"] = str(file.absolute()).replace("\\", "/")
|
||||
if "description" not in resource_info:
|
||||
resource_info["description"] = ""
|
||||
if "icon" not in resource_info:
|
||||
resource_info["icon"] = ""
|
||||
resource_info["registry_type"] = "resource"
|
||||
self.resource_type_registry.update(data)
|
||||
logger.debug(
|
||||
f"[UniLab Registry] Resource-{current_resource_number} File-{i+1}/{len(files)} "
|
||||
@@ -145,6 +182,7 @@ class Registry:
|
||||
)
|
||||
current_device_number = len(self.device_type_registry) + 1
|
||||
from unilabos.app.web.utils.action_utils import get_yaml_from_goal_type
|
||||
|
||||
for i, file in enumerate(files):
|
||||
data = yaml.safe_load(open(file, encoding="utf-8"))
|
||||
if data:
|
||||
@@ -154,6 +192,9 @@ class Registry:
|
||||
device_config["file_path"] = str(file.absolute()).replace("\\", "/")
|
||||
if "description" not in device_config:
|
||||
device_config["description"] = ""
|
||||
if "icon" not in device_config:
|
||||
device_config["icon"] = ""
|
||||
device_config["registry_type"] = "device"
|
||||
if "class" in device_config:
|
||||
# 处理状态类型
|
||||
if "status_types" in device_config["class"]:
|
||||
@@ -169,8 +210,15 @@ class Registry:
|
||||
action_config["type"] = self._replace_type_with_class(
|
||||
action_config["type"], device_id, f"动作 {action_name}"
|
||||
)
|
||||
action_config["goal_default"] = yaml.safe_load(io.StringIO(get_yaml_from_goal_type(action_config["type"].Goal)))
|
||||
action_config["schema"] = ros_action_to_json_schema(action_config["type"])
|
||||
if action_config["type"] is not None:
|
||||
action_config["goal_default"] = yaml.safe_load(
|
||||
io.StringIO(get_yaml_from_goal_type(action_config["type"].Goal))
|
||||
)
|
||||
action_config["schema"] = ros_action_to_json_schema(action_config["type"])
|
||||
else:
|
||||
logger.warning(
|
||||
f"[UniLab Registry] 设备 {device_id} 的动作 {action_name} 类型为空,跳过替换"
|
||||
)
|
||||
|
||||
self.device_type_registry.update(data)
|
||||
|
||||
@@ -188,13 +236,17 @@ class Registry:
|
||||
def obtain_registry_device_info(self):
|
||||
devices = []
|
||||
for device_id, device_info in self.device_type_registry.items():
|
||||
msg = {
|
||||
"id": device_id,
|
||||
**device_info
|
||||
}
|
||||
msg = {"id": device_id, **device_info}
|
||||
devices.append(msg)
|
||||
return devices
|
||||
|
||||
def obtain_registry_resource_info(self):
|
||||
resources = []
|
||||
for resource_id, resource_info in self.resource_type_registry.items():
|
||||
msg = {"id": resource_id, **resource_info}
|
||||
resources.append(msg)
|
||||
return resources
|
||||
|
||||
|
||||
# 全局单例实例
|
||||
lab_registry = Registry()
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
agilent_1_reservoir_290ml:
|
||||
description: Agilent 1 reservoir 290ml
|
||||
class:
|
||||
module: pylabrobot.resources.opentrons.reserviors:agilent_1_reservoir_290ml
|
||||
module: pylabrobot.resources.opentrons.reservoirs:agilent_1_reservoir_290ml
|
||||
type: pylabrobot
|
||||
|
||||
axygen_1_reservoir_90ml:
|
||||
description: Axygen 1 reservoir 90ml
|
||||
class:
|
||||
module: pylabrobot.resources.opentrons.reserviors:axygen_1_reservoir_90ml
|
||||
module: pylabrobot.resources.opentrons.reservoirs:axygen_1_reservoir_90ml
|
||||
type: pylabrobot
|
||||
|
||||
nest_12_reservoir_15ml:
|
||||
description: Nest 12 reservoir 15ml
|
||||
class:
|
||||
module: pylabrobot.resources.opentrons.reserviors:nest_12_reservoir_15ml
|
||||
module: pylabrobot.resources.opentrons.reservoirs:nest_12_reservoir_15ml
|
||||
type: pylabrobot
|
||||
|
||||
nest_1_reservoir_195ml:
|
||||
description: Nest 1 reservoir 195ml
|
||||
class:
|
||||
module: pylabrobot.resources.opentrons.reserviors:nest_1_reservoir_195ml
|
||||
module: pylabrobot.resources.opentrons.reservoirs:nest_1_reservoir_195ml
|
||||
type: pylabrobot
|
||||
|
||||
nest_1_reservoir_290ml:
|
||||
description: Nest 1 reservoir 290ml
|
||||
class:
|
||||
module: pylabrobot.resources.opentrons.reserviors:nest_1_reservoir_290ml
|
||||
module: pylabrobot.resources.opentrons.reservoirs:nest_1_reservoir_290ml
|
||||
type: pylabrobot
|
||||
|
||||
usascientific_12_reservoir_22ml:
|
||||
description: USAScientific 12 reservoir 22ml
|
||||
class:
|
||||
module: pylabrobot.resources.opentrons.reserviors:usascientific_12_reservoir_22ml
|
||||
module: pylabrobot.resources.opentrons.reservoirs:usascientific_12_reservoir_22ml
|
||||
type: pylabrobot
|
||||
|
||||
Reference in New Issue
Block a user