mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2025-12-17 13:01:12 +00:00
Cleanup registry to be easy-understanding (#76)
* delete deprecated mock devices * rename categories * combine chromatographic devices * rename rviz simulation nodes * organic virtual devices * parse vessel_id * run registry completion before merge --------- Co-authored-by: Xuwznln <18435084+Xuwznln@users.noreply.github.com>
This commit is contained in:
@@ -14,8 +14,8 @@
|
|||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "workstation",
|
"class": "workstation",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 0,
|
||||||
"y": 171,
|
"y": 0,
|
||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"nodes": [
|
"nodes": [
|
||||||
{
|
{
|
||||||
"id": "PLR_STATION",
|
"id": "liquid_handler",
|
||||||
"name": "PLR_LH_TEST",
|
"name": "liquid_handler",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "liquid_handler",
|
"class": "liquid_handler",
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
"tip_rack",
|
"tip_rack",
|
||||||
"plate_well"
|
"plate_well"
|
||||||
],
|
],
|
||||||
"parent": "PLR_STATION",
|
"parent": "liquid_handler",
|
||||||
"type": "deck",
|
"type": "deck",
|
||||||
"class": "OTDeck",
|
"class": "OTDeck",
|
||||||
"position": {
|
"position": {
|
||||||
@@ -9650,7 +9650,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "moveit.arm_slider",
|
"class": "robotic_arm.SCARA_with_slider.virtual",
|
||||||
"position": {
|
"position": {
|
||||||
"x": -500,
|
"x": -500,
|
||||||
"y": 1000,
|
"y": 1000,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "moveit.arm_slider",
|
"class": "robotic_arm.SCARA_with_slider.virtual",
|
||||||
"position": {
|
"position": {
|
||||||
"x": -500,
|
"x": -500,
|
||||||
"y": 1000,
|
"y": 1000,
|
||||||
|
|||||||
949
test/experiments/workshop.json
Normal file
949
test/experiments/workshop.json
Normal file
@@ -0,0 +1,949 @@
|
|||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "simple_station",
|
||||||
|
"name": "愚公常量合成工作站",
|
||||||
|
"children": [
|
||||||
|
"serial_pump",
|
||||||
|
"pump_reagents",
|
||||||
|
"pump_workup",
|
||||||
|
"flask_CH2Cl2",
|
||||||
|
"waste_workup",
|
||||||
|
"separator_controller",
|
||||||
|
"flask_separator",
|
||||||
|
"flask_air"
|
||||||
|
],
|
||||||
|
"parent": null,
|
||||||
|
"type": "device",
|
||||||
|
"class": "workstation",
|
||||||
|
"position": {
|
||||||
|
"x": 620.6111111111111,
|
||||||
|
"y": 171,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"protocol_type": ["PumpTransferProtocol", "CleanProtocol", "SeparateProtocol", "EvaporateProtocol"]
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "serial_pump",
|
||||||
|
"name": "serial_pump",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "device",
|
||||||
|
"class": "serial",
|
||||||
|
"position": {
|
||||||
|
"x": 620.6111111111111,
|
||||||
|
"y": 171,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"port": "COM7",
|
||||||
|
"baudrate": 9600
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pump_reagents",
|
||||||
|
"name": "pump_reagents",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "device",
|
||||||
|
"class": "syringepump.runze",
|
||||||
|
"position": {
|
||||||
|
"x": 620.6111111111111,
|
||||||
|
"y": 171,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"port": "/devices/PumpBackbone/Serial/serialwrite",
|
||||||
|
"address": "1",
|
||||||
|
"max_volume": 25.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"max_velocity": 1.0,
|
||||||
|
"position": 0.0,
|
||||||
|
"status": "Idle",
|
||||||
|
"valve_position": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_CH2Cl2",
|
||||||
|
"name": "flask_CH2Cl2",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 430.4087301587302,
|
||||||
|
"y": 428,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
{
|
||||||
|
"liquid_type": "CH2Cl2",
|
||||||
|
"liquid_volume": 1500.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_acetone",
|
||||||
|
"name": "flask_acetone",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 295.36944444444447,
|
||||||
|
"y": 428,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
{
|
||||||
|
"liquid_type": "acetone",
|
||||||
|
"liquid_volume": 1500.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_NH4Cl",
|
||||||
|
"name": "flask_NH4Cl",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 165.36944444444444,
|
||||||
|
"y": 428,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
{
|
||||||
|
"liquid_type": "NH4Cl",
|
||||||
|
"liquid_volume": 1500.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_grignard",
|
||||||
|
"name": "flask_grignard",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 165.36944444444444,
|
||||||
|
"y": 428,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
{
|
||||||
|
"liquid_type": "grignard",
|
||||||
|
"liquid_volume": 1500.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_THF",
|
||||||
|
"name": "flask_THF",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 35,
|
||||||
|
"y": 428,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
{
|
||||||
|
"liquid_type": "THF",
|
||||||
|
"liquid_volume": 1500.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "reactor",
|
||||||
|
"name": "reactor",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 698.1111111111111,
|
||||||
|
"y": 428,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 5000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "stirrer",
|
||||||
|
"name": "stirrer",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "device",
|
||||||
|
"class": "heaterstirrer.dalong",
|
||||||
|
"position": {
|
||||||
|
"x": 698.1111111111111,
|
||||||
|
"y": 478,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"port": "COM43",
|
||||||
|
"temp_warning": 60.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"status": "Idle",
|
||||||
|
"temp": 0.0,
|
||||||
|
"stir_speed": 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pump_workup",
|
||||||
|
"name": "pump_workup",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "device",
|
||||||
|
"class": "syringepump.runze",
|
||||||
|
"position": {
|
||||||
|
"x": 1195.611507936508,
|
||||||
|
"y": 686,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"port": "/devices/PumpBackbone/Serial/serialwrite",
|
||||||
|
"address": "2",
|
||||||
|
"max_volume": 25.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"max_velocity": 1.0,
|
||||||
|
"position": 0.0,
|
||||||
|
"status": "Idle",
|
||||||
|
"valve_position": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "waste_workup",
|
||||||
|
"name": "waste_workup",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 1587.703373015873,
|
||||||
|
"y": 1172.5,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "separator_controller",
|
||||||
|
"name": "separator_controller",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "device",
|
||||||
|
"class": "separator.homemade",
|
||||||
|
"position": {
|
||||||
|
"x": 1624.4027777777778,
|
||||||
|
"y": 665.5,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"port_executor": "/dev/tty.usbserial-11140",
|
||||||
|
"port_sensor": "/dev/tty.usbserial-11130"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"sensordata": 0.0,
|
||||||
|
"status": "Idle"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_separator",
|
||||||
|
"name": "flask_separator",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 1614.404365079365,
|
||||||
|
"y": 948,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_holding",
|
||||||
|
"name": "flask_holding",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 1915.7035714285714,
|
||||||
|
"y": 665.5,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_H2O",
|
||||||
|
"name": "flask_H2O",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 1785.7035714285714,
|
||||||
|
"y": 665.5,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
{
|
||||||
|
"liquid_type": "H2O",
|
||||||
|
"liquid_volume": 1500.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_NaHCO3",
|
||||||
|
"name": "flask_NaHCO3",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 2054.0650793650793,
|
||||||
|
"y": 665.5,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
{
|
||||||
|
"liquid_type": "NaHCO3",
|
||||||
|
"liquid_volume": 1500.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pump_column",
|
||||||
|
"name": "pump_column",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "device",
|
||||||
|
"class": "syringepump.runze",
|
||||||
|
"position": {
|
||||||
|
"x": 1630.6527777777778,
|
||||||
|
"y": 448.5,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"port": "/devices/PumpBackbone/Serial/serialwrite",
|
||||||
|
"address": "3",
|
||||||
|
"max_volume": 25.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"max_velocity": 1.0,
|
||||||
|
"position": 0.0,
|
||||||
|
"status": "Idle",
|
||||||
|
"valve_position": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rotavap",
|
||||||
|
"name": "rotavap",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "device",
|
||||||
|
"class": "rotavap",
|
||||||
|
"position": {
|
||||||
|
"x": 1339.7031746031746,
|
||||||
|
"y": 968.5,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"port": "COM15"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"temperature": 0.0,
|
||||||
|
"rotate_time": 0.0,
|
||||||
|
"status": "Idle"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_rv",
|
||||||
|
"name": "flask_rv",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 1339.7031746031746,
|
||||||
|
"y": 1152,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "column",
|
||||||
|
"name": "column",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 909.722619047619,
|
||||||
|
"y": 948,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 200.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_column",
|
||||||
|
"name": "flask_column",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 867.972619047619,
|
||||||
|
"y": 1152,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_air",
|
||||||
|
"name": "flask_air",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 742.722619047619,
|
||||||
|
"y": 948,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dry_column",
|
||||||
|
"name": "dry_column",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 1206.722619047619,
|
||||||
|
"y": 948,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 200.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_dry_column",
|
||||||
|
"name": "flask_dry_column",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 1148.222619047619,
|
||||||
|
"y": 1152,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pump_ext",
|
||||||
|
"name": "pump_ext",
|
||||||
|
"children": [],
|
||||||
|
"parent": "simple_station",
|
||||||
|
"type": "device",
|
||||||
|
"class": "syringepump.runze",
|
||||||
|
"position": {
|
||||||
|
"x": 1469.7031746031746,
|
||||||
|
"y": 968.5,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"port": "/devices/PumpBackbone/Serial/serialwrite",
|
||||||
|
"address": "4",
|
||||||
|
"max_volume": 25.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"max_velocity": 1.0,
|
||||||
|
"position": 0.0,
|
||||||
|
"status": "Idle",
|
||||||
|
"valve_position": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "AGV",
|
||||||
|
"name": "AGV",
|
||||||
|
"children": ["zhixing_agv", "zhixing_ur_arm"],
|
||||||
|
"parent": null,
|
||||||
|
"type": "device",
|
||||||
|
"class": "workstation",
|
||||||
|
"position": {
|
||||||
|
"x": 698.1111111111111,
|
||||||
|
"y": 478,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"protocol_type": ["AGVTransferProtocol"]
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "zhixing_agv",
|
||||||
|
"name": "zhixing_agv",
|
||||||
|
"children": [],
|
||||||
|
"parent": "AGV",
|
||||||
|
"type": "device",
|
||||||
|
"class": "zhixing_agv",
|
||||||
|
"position": {
|
||||||
|
"x": 698.1111111111111,
|
||||||
|
"y": 478,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"host": "192.168.1.42"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "zhixing_ur_arm",
|
||||||
|
"name": "zhixing_ur_arm",
|
||||||
|
"children": [],
|
||||||
|
"parent": "AGV",
|
||||||
|
"type": "device",
|
||||||
|
"class": "zhixing_ur_arm",
|
||||||
|
"position": {
|
||||||
|
"x": 698.1111111111111,
|
||||||
|
"y": 478,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"host": "192.168.1.178"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"source": "pump_reagents",
|
||||||
|
"target": "serial_pump",
|
||||||
|
"type": "communication",
|
||||||
|
"port": {
|
||||||
|
"pump_reagents": "port",
|
||||||
|
"serial_pump": "port"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_workup",
|
||||||
|
"target": "serial_pump",
|
||||||
|
"type": "communication",
|
||||||
|
"port": {
|
||||||
|
"pump_reagents": "port",
|
||||||
|
"serial_pump": "port"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_column",
|
||||||
|
"target": "serial_pump",
|
||||||
|
"type": "communication",
|
||||||
|
"port": {
|
||||||
|
"pump_reagents": "port",
|
||||||
|
"serial_pump": "port"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_ext",
|
||||||
|
"target": "serial_pump",
|
||||||
|
"type": "communication",
|
||||||
|
"port": {
|
||||||
|
"pump_reagents": "port",
|
||||||
|
"serial_pump": "port"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "reactor",
|
||||||
|
"target": "pump_reagents",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"reactor": "top",
|
||||||
|
"pump_reagents": "5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "rotavap",
|
||||||
|
"target": "flask_rv",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"rotavap": "bottom",
|
||||||
|
"flask_rv": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "separator_controller",
|
||||||
|
"target": "flask_separator",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"separator_controller": "bottom",
|
||||||
|
"flask_separator": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "column",
|
||||||
|
"target": "flask_column",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"column": "bottom",
|
||||||
|
"flask_column": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "dry_column",
|
||||||
|
"target": "flask_dry_column",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"dry_column": "bottom",
|
||||||
|
"flask_dry_column": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_ext",
|
||||||
|
"target": "pump_column",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_ext": "8",
|
||||||
|
"pump_column": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_ext",
|
||||||
|
"target": "waste_workup",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_ext": "2",
|
||||||
|
"waste_workup": "-1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_reagents",
|
||||||
|
"target": "flask_THF",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_reagents": "7",
|
||||||
|
"flask_THF": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_reagents",
|
||||||
|
"target": "flask_NH4Cl",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_reagents": "4",
|
||||||
|
"flask_NH4Cl": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_reagents",
|
||||||
|
"target": "flask_CH2Cl2",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_reagents": "2",
|
||||||
|
"flask_CH2Cl2": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_reagents",
|
||||||
|
"target": "flask_acetone",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_reagents": "3",
|
||||||
|
"flask_acetone": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_reagents",
|
||||||
|
"target": "pump_workup",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_reagents": "1",
|
||||||
|
"pump_workup": "8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_reagents",
|
||||||
|
"target": "flask_grignard",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_reagents": "6",
|
||||||
|
"flask_grignard": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_reagents",
|
||||||
|
"target": "reactor",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_reagents": "5",
|
||||||
|
"reactor": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_reagents",
|
||||||
|
"target": "flask_air",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_reagents": "8",
|
||||||
|
"flask_air": "-1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_workup",
|
||||||
|
"target": "waste_workup",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_workup": "2",
|
||||||
|
"waste_workup": "-1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_workup",
|
||||||
|
"target": "flask_H2O",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_workup": "7",
|
||||||
|
"flask_H2O": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_workup",
|
||||||
|
"target": "flask_NaHCO3",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_workup": "6",
|
||||||
|
"flask_NaHCO3": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_workup",
|
||||||
|
"target": "pump_reagents",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_workup": "8",
|
||||||
|
"pump_reagents": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_workup",
|
||||||
|
"target": "flask_holding",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_workup": "5",
|
||||||
|
"flask_holding": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_workup",
|
||||||
|
"target": "separator_controller",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_workup": "4",
|
||||||
|
"separator_controller": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_workup",
|
||||||
|
"target": "flask_separator",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_workup": "3",
|
||||||
|
"flask_separator": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_workup",
|
||||||
|
"target": "pump_column",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_workup": "1",
|
||||||
|
"pump_column": "8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_column",
|
||||||
|
"target": "column",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_column": "4",
|
||||||
|
"column": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_column",
|
||||||
|
"target": "flask_column",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_column": "3",
|
||||||
|
"flask_column": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_column",
|
||||||
|
"target": "rotavap",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_column": "2",
|
||||||
|
"rotavap": "-1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_column",
|
||||||
|
"target": "pump_workup",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_column": "8",
|
||||||
|
"pump_workup": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_column",
|
||||||
|
"target": "flask_air",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_column": "5",
|
||||||
|
"flask_air": "-1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_column",
|
||||||
|
"target": "dry_column",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_column": "7",
|
||||||
|
"dry_column": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_column",
|
||||||
|
"target": "flask_dry_column",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_column": "6",
|
||||||
|
"flask_dry_column": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "pump_column",
|
||||||
|
"target": "pump_ext",
|
||||||
|
"type": "physical",
|
||||||
|
"port": {
|
||||||
|
"pump_column": "1",
|
||||||
|
"pump_ext": "8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import networkx as nx
|
|||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
from typing import List, Dict, Any, Union
|
from typing import List, Dict, Any, Union
|
||||||
|
from .utils.vessel_parser import get_vessel
|
||||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -346,16 +347,7 @@ def generate_add_protocol(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
# 🔧 核心修改:从字典中提取容器ID
|
||||||
# 统一处理vessel参数
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
if isinstance(vessel, dict):
|
|
||||||
if "id" not in vessel:
|
|
||||||
vessel_id = list(vessel.values())[0].get("id", "")
|
|
||||||
else:
|
|
||||||
vessel_id = vessel.get("id", "")
|
|
||||||
vessel_data = vessel.get("data", {})
|
|
||||||
else:
|
|
||||||
vessel_id = str(vessel)
|
|
||||||
vessel_data = G.nodes[vessel_id].get("data", {}) if vessel_id in G.nodes() else {}
|
|
||||||
|
|
||||||
# 🔧 修改:更新容器的液体体积(假设有 liquid_volume 字段)
|
# 🔧 修改:更新容器的液体体积(假设有 liquid_volume 字段)
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import networkx as nx
|
import networkx as nx
|
||||||
import logging
|
import logging
|
||||||
from typing import List, Dict, Any, Union
|
from typing import List, Dict, Any, Union
|
||||||
|
from .utils.vessel_parser import get_vessel
|
||||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -235,16 +236,7 @@ def generate_adjust_ph_protocol(
|
|||||||
List[Dict[str, Any]]: 动作序列
|
List[Dict[str, Any]]: 动作序列
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 统一处理vessel参数
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
if isinstance(vessel, dict):
|
|
||||||
if "id" not in vessel:
|
|
||||||
vessel_id = list(vessel.values())[0].get("id", "")
|
|
||||||
else:
|
|
||||||
vessel_id = vessel.get("id", "")
|
|
||||||
vessel_data = vessel.get("data", {})
|
|
||||||
else:
|
|
||||||
vessel_id = str(vessel)
|
|
||||||
vessel_data = G.nodes[vessel_id].get("data", {}) if vessel_id in G.nodes() else {}
|
|
||||||
|
|
||||||
if not vessel_id:
|
if not vessel_id:
|
||||||
debug_print(f"❌ vessel 参数无效,必须包含id字段或直接提供容器ID. vessel: {vessel}")
|
debug_print(f"❌ vessel 参数无效,必须包含id字段或直接提供容器ID. vessel: {vessel}")
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
|
from .utils.vessel_parser import get_vessel
|
||||||
from .pump_protocol import generate_pump_protocol
|
from .pump_protocol import generate_pump_protocol
|
||||||
|
|
||||||
|
|
||||||
@@ -181,16 +182,7 @@ def generate_clean_vessel_protocol(
|
|||||||
clean_protocol = generate_clean_vessel_protocol(G, {"id": "main_reactor"}, "water", 100.0, 60.0, 2)
|
clean_protocol = generate_clean_vessel_protocol(G, {"id": "main_reactor"}, "water", 100.0, 60.0, 2)
|
||||||
"""
|
"""
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
# 🔧 核心修改:从字典中提取容器ID
|
||||||
# 统一处理vessel参数
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
if isinstance(vessel, dict):
|
|
||||||
if "id" not in vessel:
|
|
||||||
vessel_id = list(vessel.values())[0].get("id", "")
|
|
||||||
else:
|
|
||||||
vessel_id = vessel.get("id", "")
|
|
||||||
vessel_data = vessel.get("data", {})
|
|
||||||
else:
|
|
||||||
vessel_id = str(vessel)
|
|
||||||
vessel_data = G.nodes[vessel_id].get("data", {}) if vessel_id in G.nodes() else {}
|
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import logging
|
|||||||
import uuid
|
import uuid
|
||||||
import sys
|
import sys
|
||||||
from typing import List, Dict, Any, Optional
|
from typing import List, Dict, Any, Optional
|
||||||
|
from .utils.vessel_parser import get_vessel
|
||||||
from .pump_protocol import generate_pump_protocol_with_rinsing, generate_pump_protocol
|
from .pump_protocol import generate_pump_protocol_with_rinsing, generate_pump_protocol
|
||||||
|
|
||||||
# 设置日志
|
# 设置日志
|
||||||
@@ -288,16 +289,7 @@ def generate_evacuateandrefill_protocol(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
# 🔧 核心修改:从字典中提取容器ID
|
||||||
# 统一处理vessel参数
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
if isinstance(vessel, dict):
|
|
||||||
if "id" not in vessel:
|
|
||||||
vessel_id = list(vessel.values())[0].get("id", "")
|
|
||||||
else:
|
|
||||||
vessel_id = vessel.get("id", "")
|
|
||||||
vessel_data = vessel.get("data", {})
|
|
||||||
else:
|
|
||||||
vessel_id = str(vessel)
|
|
||||||
vessel_data = G.nodes[vessel_id].get("data", {}) if vessel_id in G.nodes() else {}
|
|
||||||
|
|
||||||
# 硬编码重复次数为 3
|
# 硬编码重复次数为 3
|
||||||
repeats = 3
|
repeats = 3
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from typing import List, Dict, Any, Optional, Union
|
|||||||
import networkx as nx
|
import networkx as nx
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
from .utils.vessel_parser import get_vessel
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -201,16 +202,7 @@ def generate_evaporate_protocol(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
# 🔧 核心修改:从字典中提取容器ID
|
||||||
# 统一处理vessel参数
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
if isinstance(vessel, dict):
|
|
||||||
if "id" not in vessel:
|
|
||||||
vessel_id = list(vessel.values())[0].get("id", "")
|
|
||||||
else:
|
|
||||||
vessel_id = vessel.get("id", "")
|
|
||||||
vessel_data = vessel.get("data", {})
|
|
||||||
else:
|
|
||||||
vessel_id = str(vessel)
|
|
||||||
vessel_data = G.nodes[vessel_id].get("data", {}) if vessel_id in G.nodes() else {}
|
|
||||||
|
|
||||||
debug_print("🌟" * 20)
|
debug_print("🌟" * 20)
|
||||||
debug_print("🌪️ 开始生成蒸发协议(支持单位和体积运算)✨")
|
debug_print("🌪️ 开始生成蒸发协议(支持单位和体积运算)✨")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from typing import List, Dict, Any, Optional
|
from typing import List, Dict, Any, Optional
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
import logging
|
import logging
|
||||||
|
from .utils.vessel_parser import get_vessel
|
||||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -68,16 +69,7 @@ def generate_filter_protocol(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
# 🔧 核心修改:从字典中提取容器ID
|
||||||
# 统一处理vessel参数
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
if isinstance(vessel, dict):
|
|
||||||
if "id" not in vessel:
|
|
||||||
vessel_id = list(vessel.values())[0].get("id", "")
|
|
||||||
else:
|
|
||||||
vessel_id = vessel.get("id", "")
|
|
||||||
vessel_data = vessel.get("data", {})
|
|
||||||
else:
|
|
||||||
vessel_id = str(vessel)
|
|
||||||
vessel_data = G.nodes[vessel_id].get("data", {}) if vessel_id in G.nodes() else {}
|
|
||||||
|
|
||||||
debug_print("🌊" * 20)
|
debug_print("🌊" * 20)
|
||||||
debug_print("🚀 开始生成过滤协议(支持体积运算)✨")
|
debug_print("🚀 开始生成过滤协议(支持体积运算)✨")
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from typing import List, Dict, Any, Union
|
|||||||
import networkx as nx
|
import networkx as nx
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
from .utils.vessel_parser import get_vessel
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -217,16 +218,7 @@ def generate_heat_chill_protocol(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
# 🔧 核心修改:从字典中提取容器ID
|
||||||
# 统一处理vessel参数
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
if isinstance(vessel, dict):
|
|
||||||
if "id" not in vessel:
|
|
||||||
vessel_id = list(vessel.values())[0].get("id", "")
|
|
||||||
else:
|
|
||||||
vessel_id = vessel.get("id", "")
|
|
||||||
vessel_data = vessel.get("data", {})
|
|
||||||
else:
|
|
||||||
vessel_id = str(vessel)
|
|
||||||
vessel_data = G.nodes[vessel_id].get("data", {}) if vessel_id in G.nodes() else {}
|
|
||||||
|
|
||||||
debug_print("🌡️" * 20)
|
debug_print("🌡️" * 20)
|
||||||
debug_print("🚀 开始生成加热冷却协议(支持vessel字典)✨")
|
debug_print("🚀 开始生成加热冷却协议(支持vessel字典)✨")
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import networkx as nx
|
import networkx as nx
|
||||||
from typing import List, Dict, Any, Optional
|
from typing import List, Dict, Any, Optional
|
||||||
|
from .utils.vessel_parser import get_vessel
|
||||||
|
|
||||||
|
|
||||||
def parse_temperature(temp_str: str) -> float:
|
def parse_temperature(temp_str: str) -> float:
|
||||||
@@ -170,16 +171,7 @@ def generate_hydrogenate_protocol(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
# 🔧 核心修改:从字典中提取容器ID
|
||||||
# 统一处理vessel参数
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
if isinstance(vessel, dict):
|
|
||||||
if "id" not in vessel:
|
|
||||||
vessel_id = list(vessel.values())[0].get("id", "")
|
|
||||||
else:
|
|
||||||
vessel_id = vessel.get("id", "")
|
|
||||||
vessel_data = vessel.get("data", {})
|
|
||||||
else:
|
|
||||||
vessel_id = str(vessel)
|
|
||||||
vessel_data = G.nodes[vessel_id].get("data", {}) if vessel_id in G.nodes() else {}
|
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import networkx as nx
|
|||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
from typing import List, Dict, Any, Tuple, Union
|
from typing import List, Dict, Any, Tuple, Union
|
||||||
|
from .utils.vessel_parser import get_vessel
|
||||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -287,16 +288,7 @@ def generate_recrystallize_protocol(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
# 🔧 核心修改:从字典中提取容器ID
|
||||||
# 统一处理vessel参数
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
if isinstance(vessel, dict):
|
|
||||||
if "id" not in vessel:
|
|
||||||
vessel_id = list(vessel.values())[0].get("id", "")
|
|
||||||
else:
|
|
||||||
vessel_id = vessel.get("id", "")
|
|
||||||
vessel_data = vessel.get("data", {})
|
|
||||||
else:
|
|
||||||
vessel_id = str(vessel)
|
|
||||||
vessel_data = G.nodes[vessel_id].get("data", {}) if vessel_id in G.nodes() else {}
|
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import re
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from typing import List, Dict, Any, Union
|
from typing import List, Dict, Any, Union
|
||||||
|
from .utils.vessel_parser import get_vessel
|
||||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -63,6 +64,461 @@ def create_action_log(message: str, emoji: str = "📝") -> Dict[str, Any]:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def generate_separate_protocol(
|
||||||
|
G: nx.DiGraph,
|
||||||
|
# 🔧 基础参数,支持XDL的vessel参数
|
||||||
|
vessel: dict = None, # 🔧 修改:从字符串改为字典类型
|
||||||
|
purpose: str = "separate", # 分离目的
|
||||||
|
product_phase: str = "top", # 产物相
|
||||||
|
# 🔧 可选的详细参数
|
||||||
|
from_vessel: Union[str, dict] = "", # 源容器(通常在separate前已经transfer了)
|
||||||
|
separation_vessel: Union[str, dict] = "", # 分离容器(与vessel同义)
|
||||||
|
to_vessel: Union[str, dict] = "", # 目标容器(可选)
|
||||||
|
waste_phase_to_vessel: Union[str, dict] = "", # 废相目标容器
|
||||||
|
product_vessel: Union[str, dict] = "", # XDL: 产物容器(与to_vessel同义)
|
||||||
|
waste_vessel: Union[str, dict] = "", # XDL: 废液容器(与waste_phase_to_vessel同义)
|
||||||
|
# 🔧 溶剂相关参数
|
||||||
|
solvent: str = "", # 溶剂名称
|
||||||
|
solvent_volume: Union[str, float] = 0.0, # 溶剂体积
|
||||||
|
volume: Union[str, float] = 0.0, # XDL: 体积(与solvent_volume同义)
|
||||||
|
# 🔧 操作参数
|
||||||
|
through: str = "", # 通过材料
|
||||||
|
repeats: int = 1, # 重复次数
|
||||||
|
stir_time: float = 30.0, # 搅拌时间(秒)
|
||||||
|
stir_speed: float = 300.0, # 搅拌速度
|
||||||
|
settling_time: float = 300.0, # 沉降时间(秒)
|
||||||
|
**kwargs
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
生成分离操作的协议序列 - 支持vessel字典和体积运算
|
||||||
|
|
||||||
|
支持XDL参数格式:
|
||||||
|
- vessel: 分离容器字典(必需)
|
||||||
|
- purpose: "wash", "extract", "separate"
|
||||||
|
- product_phase: "top", "bottom"
|
||||||
|
- product_vessel: 产物收集容器
|
||||||
|
- waste_vessel: 废液收集容器
|
||||||
|
- solvent: 溶剂名称
|
||||||
|
- volume: "200 mL", "?" 或数值
|
||||||
|
- repeats: 重复次数
|
||||||
|
|
||||||
|
分离流程:
|
||||||
|
1. (可选)添加溶剂到分离容器
|
||||||
|
2. 搅拌混合
|
||||||
|
3. 静置分层
|
||||||
|
4. 收集指定相到目标容器
|
||||||
|
5. 重复指定次数
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 🔧 核心修改:vessel参数兼容处理
|
||||||
|
if vessel is None:
|
||||||
|
if isinstance(separation_vessel, dict):
|
||||||
|
vessel = separation_vessel
|
||||||
|
else:
|
||||||
|
raise ValueError("必须提供vessel字典参数")
|
||||||
|
|
||||||
|
# 🔧 核心修改:从字典中提取容器ID
|
||||||
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
|
|
||||||
|
debug_print("🌀" * 20)
|
||||||
|
debug_print("🚀 开始生成分离协议(支持vessel字典和体积运算)✨")
|
||||||
|
debug_print(f"📝 输入参数:")
|
||||||
|
debug_print(f" 🥽 vessel: {vessel} (ID: {vessel_id})")
|
||||||
|
debug_print(f" 🎯 分离目的: '{purpose}'")
|
||||||
|
debug_print(f" 📊 产物相: '{product_phase}'")
|
||||||
|
debug_print(f" 💧 溶剂: '{solvent}'")
|
||||||
|
debug_print(f" 📏 体积: {volume} (类型: {type(volume)})")
|
||||||
|
debug_print(f" 🔄 重复次数: {repeats}")
|
||||||
|
debug_print(f" 🎯 产物容器: '{product_vessel}'")
|
||||||
|
debug_print(f" 🗑️ 废液容器: '{waste_vessel}'")
|
||||||
|
debug_print(f" 📦 其他参数: {kwargs}")
|
||||||
|
debug_print("🌀" * 20)
|
||||||
|
|
||||||
|
action_sequence = []
|
||||||
|
|
||||||
|
# 🔧 新增:记录分离前的容器状态
|
||||||
|
debug_print("🔍 记录分离前容器状态...")
|
||||||
|
original_liquid_volume = get_vessel_liquid_volume(vessel)
|
||||||
|
debug_print(f"📊 分离前液体体积: {original_liquid_volume:.2f}mL")
|
||||||
|
|
||||||
|
# === 参数验证和标准化 ===
|
||||||
|
debug_print("🔍 步骤1: 参数验证和标准化...")
|
||||||
|
action_sequence.append(create_action_log(f"开始分离操作 - 容器: {vessel_id}", "🎬"))
|
||||||
|
action_sequence.append(create_action_log(f"分离目的: {purpose}", "🧪"))
|
||||||
|
action_sequence.append(create_action_log(f"产物相: {product_phase}", "📊"))
|
||||||
|
|
||||||
|
# 统一容器参数 - 支持字典和字符串
|
||||||
|
def extract_vessel_id(vessel_param):
|
||||||
|
if isinstance(vessel_param, dict):
|
||||||
|
return vessel_param.get("id", "")
|
||||||
|
elif isinstance(vessel_param, str):
|
||||||
|
return vessel_param
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
final_vessel_id, _ = vessel_id
|
||||||
|
final_to_vessel_id, _ = get_vessel(to_vessel) or get_vessel(product_vessel)
|
||||||
|
final_waste_vessel_id, _ = get_vessel(waste_phase_to_vessel) or get_vessel(waste_vessel)
|
||||||
|
|
||||||
|
# 统一体积参数
|
||||||
|
final_volume = parse_volume_input(volume or solvent_volume)
|
||||||
|
|
||||||
|
# 🔧 修复:确保repeats至少为1
|
||||||
|
if repeats <= 0:
|
||||||
|
repeats = 1
|
||||||
|
debug_print(f"⚠️ 重复次数参数 <= 0,自动设置为 1")
|
||||||
|
|
||||||
|
debug_print(f"🔧 标准化后的参数:")
|
||||||
|
debug_print(f" 🥼 分离容器: '{final_vessel_id}'")
|
||||||
|
debug_print(f" 🎯 产物容器: '{final_to_vessel_id}'")
|
||||||
|
debug_print(f" 🗑️ 废液容器: '{final_waste_vessel_id}'")
|
||||||
|
debug_print(f" 📏 溶剂体积: {final_volume}mL")
|
||||||
|
debug_print(f" 🔄 重复次数: {repeats}")
|
||||||
|
|
||||||
|
action_sequence.append(create_action_log(f"分离容器: {final_vessel_id}", "🧪"))
|
||||||
|
action_sequence.append(create_action_log(f"溶剂体积: {final_volume}mL", "📏"))
|
||||||
|
action_sequence.append(create_action_log(f"重复次数: {repeats}", "🔄"))
|
||||||
|
|
||||||
|
# 验证必需参数
|
||||||
|
if not purpose:
|
||||||
|
purpose = "separate"
|
||||||
|
if not product_phase:
|
||||||
|
product_phase = "top"
|
||||||
|
if purpose not in ["wash", "extract", "separate"]:
|
||||||
|
debug_print(f"⚠️ 未知的分离目的 '{purpose}',使用默认值 'separate'")
|
||||||
|
purpose = "separate"
|
||||||
|
action_sequence.append(create_action_log(f"未知目的,使用: {purpose}", "⚠️"))
|
||||||
|
if product_phase not in ["top", "bottom"]:
|
||||||
|
debug_print(f"⚠️ 未知的产物相 '{product_phase}',使用默认值 'top'")
|
||||||
|
product_phase = "top"
|
||||||
|
action_sequence.append(create_action_log(f"未知相别,使用: {product_phase}", "⚠️"))
|
||||||
|
|
||||||
|
debug_print("✅ 参数验证通过")
|
||||||
|
action_sequence.append(create_action_log("参数验证通过", "✅"))
|
||||||
|
|
||||||
|
# === 查找设备 ===
|
||||||
|
debug_print("🔍 步骤2: 查找设备...")
|
||||||
|
action_sequence.append(create_action_log("正在查找相关设备...", "🔍"))
|
||||||
|
|
||||||
|
# 查找分离器设备
|
||||||
|
separator_device = find_separator_device(G, final_vessel_id) # 🔧 使用 final_vessel_id
|
||||||
|
if separator_device:
|
||||||
|
action_sequence.append(create_action_log(f"找到分离器设备: {separator_device}", "🧪"))
|
||||||
|
else:
|
||||||
|
debug_print("⚠️ 未找到分离器设备,可能无法执行分离")
|
||||||
|
action_sequence.append(create_action_log("未找到分离器设备", "⚠️"))
|
||||||
|
|
||||||
|
# 查找搅拌器
|
||||||
|
stirrer_device = find_connected_stirrer(G, final_vessel_id) # 🔧 使用 final_vessel_id
|
||||||
|
if stirrer_device:
|
||||||
|
action_sequence.append(create_action_log(f"找到搅拌器: {stirrer_device}", "🌪️"))
|
||||||
|
else:
|
||||||
|
action_sequence.append(create_action_log("未找到搅拌器", "⚠️"))
|
||||||
|
|
||||||
|
# 查找溶剂容器(如果需要)
|
||||||
|
solvent_vessel = ""
|
||||||
|
if solvent and solvent.strip():
|
||||||
|
solvent_vessel = find_solvent_vessel(G, solvent)
|
||||||
|
if solvent_vessel:
|
||||||
|
action_sequence.append(create_action_log(f"找到溶剂容器: {solvent_vessel}", "💧"))
|
||||||
|
else:
|
||||||
|
action_sequence.append(create_action_log(f"未找到溶剂容器: {solvent}", "⚠️"))
|
||||||
|
|
||||||
|
debug_print(f"📊 设备配置:")
|
||||||
|
debug_print(f" 🧪 分离器设备: '{separator_device}'")
|
||||||
|
debug_print(f" 🌪️ 搅拌器设备: '{stirrer_device}'")
|
||||||
|
debug_print(f" 💧 溶剂容器: '{solvent_vessel}'")
|
||||||
|
|
||||||
|
# === 执行分离流程 ===
|
||||||
|
debug_print("🔍 步骤3: 执行分离流程...")
|
||||||
|
action_sequence.append(create_action_log("开始分离工作流程", "🎯"))
|
||||||
|
|
||||||
|
# 🔧 新增:体积变化跟踪变量
|
||||||
|
current_volume = original_liquid_volume
|
||||||
|
|
||||||
|
try:
|
||||||
|
for repeat_idx in range(repeats):
|
||||||
|
cycle_num = repeat_idx + 1
|
||||||
|
debug_print(f"🔄 第{cycle_num}轮: 开始分离循环 {cycle_num}/{repeats}")
|
||||||
|
action_sequence.append(create_action_log(f"分离循环 {cycle_num}/{repeats} 开始", "🔄"))
|
||||||
|
|
||||||
|
# 步骤3.1: 添加溶剂(如果需要)
|
||||||
|
if solvent_vessel and final_volume > 0:
|
||||||
|
debug_print(f"🔄 第{cycle_num}轮 步骤1: 添加溶剂 {solvent} ({final_volume}mL)")
|
||||||
|
action_sequence.append(create_action_log(f"向分离容器添加 {final_volume}mL {solvent}", "💧"))
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 使用pump protocol添加溶剂
|
||||||
|
pump_actions = generate_pump_protocol_with_rinsing(
|
||||||
|
G=G,
|
||||||
|
from_vessel=solvent_vessel,
|
||||||
|
to_vessel=final_vessel_id, # 🔧 使用 final_vessel_id
|
||||||
|
volume=final_volume,
|
||||||
|
amount="",
|
||||||
|
time=0.0,
|
||||||
|
viscous=False,
|
||||||
|
rinsing_solvent="",
|
||||||
|
rinsing_volume=0.0,
|
||||||
|
rinsing_repeats=0,
|
||||||
|
solid=False,
|
||||||
|
flowrate=2.5,
|
||||||
|
transfer_flowrate=0.5,
|
||||||
|
rate_spec="",
|
||||||
|
event="",
|
||||||
|
through="",
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
action_sequence.extend(pump_actions)
|
||||||
|
debug_print(f"✅ 溶剂添加完成,添加了 {len(pump_actions)} 个动作")
|
||||||
|
action_sequence.append(create_action_log(f"溶剂转移完成 ({len(pump_actions)} 个操作)", "✅"))
|
||||||
|
|
||||||
|
# 🔧 新增:更新体积 - 添加溶剂后
|
||||||
|
current_volume += final_volume
|
||||||
|
update_vessel_volume(vessel, G, current_volume, f"添加{final_volume}mL {solvent}后")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
debug_print(f"❌ 溶剂添加失败: {str(e)}")
|
||||||
|
action_sequence.append(create_action_log(f"溶剂添加失败: {str(e)}", "❌"))
|
||||||
|
else:
|
||||||
|
debug_print(f"🔄 第{cycle_num}轮 步骤1: 无需添加溶剂")
|
||||||
|
action_sequence.append(create_action_log("无需添加溶剂", "⏭️"))
|
||||||
|
|
||||||
|
# 步骤3.2: 启动搅拌(如果有搅拌器)
|
||||||
|
if stirrer_device and stir_time > 0:
|
||||||
|
debug_print(f"🔄 第{cycle_num}轮 步骤2: 开始搅拌 ({stir_speed}rpm,持续 {stir_time}s)")
|
||||||
|
action_sequence.append(create_action_log(f"开始搅拌: {stir_speed}rpm,持续 {stir_time}s", "🌪️"))
|
||||||
|
|
||||||
|
action_sequence.append({
|
||||||
|
"device_id": stirrer_device,
|
||||||
|
"action_name": "start_stir",
|
||||||
|
"action_kwargs": {
|
||||||
|
"vessel": final_vessel_id, # 🔧 使用 final_vessel_id
|
||||||
|
"stir_speed": stir_speed,
|
||||||
|
"purpose": f"分离混合 - {purpose}"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# 搅拌等待
|
||||||
|
stir_minutes = stir_time / 60
|
||||||
|
action_sequence.append(create_action_log(f"搅拌中,持续 {stir_minutes:.1f} 分钟", "⏱️"))
|
||||||
|
action_sequence.append({
|
||||||
|
"action_name": "wait",
|
||||||
|
"action_kwargs": {"time": stir_time}
|
||||||
|
})
|
||||||
|
|
||||||
|
# 停止搅拌
|
||||||
|
action_sequence.append(create_action_log("停止搅拌器", "🛑"))
|
||||||
|
action_sequence.append({
|
||||||
|
"device_id": stirrer_device,
|
||||||
|
"action_name": "stop_stir",
|
||||||
|
"action_kwargs": {"vessel": final_vessel_id} # 🔧 使用 final_vessel_id
|
||||||
|
})
|
||||||
|
|
||||||
|
else:
|
||||||
|
debug_print(f"🔄 第{cycle_num}轮 步骤2: 无需搅拌")
|
||||||
|
action_sequence.append(create_action_log("无需搅拌", "⏭️"))
|
||||||
|
|
||||||
|
# 步骤3.3: 静置分层
|
||||||
|
if settling_time > 0:
|
||||||
|
debug_print(f"🔄 第{cycle_num}轮 步骤3: 静置分层 ({settling_time}s)")
|
||||||
|
settling_minutes = settling_time / 60
|
||||||
|
action_sequence.append(create_action_log(f"静置分层 ({settling_minutes:.1f} 分钟)", "⚖️"))
|
||||||
|
action_sequence.append({
|
||||||
|
"action_name": "wait",
|
||||||
|
"action_kwargs": {"time": settling_time}
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
debug_print(f"🔄 第{cycle_num}轮 步骤3: 未指定静置时间")
|
||||||
|
action_sequence.append(create_action_log("未指定静置时间", "⏭️"))
|
||||||
|
|
||||||
|
# 步骤3.4: 执行分离操作
|
||||||
|
if separator_device:
|
||||||
|
debug_print(f"🔄 第{cycle_num}轮 步骤4: 执行分离操作")
|
||||||
|
action_sequence.append(create_action_log(f"执行分离: 收集{product_phase}相", "🧪"))
|
||||||
|
|
||||||
|
# 🔧 替换为具体的分离操作逻辑(基于old版本)
|
||||||
|
|
||||||
|
# 首先进行分液判断(电导突跃)
|
||||||
|
action_sequence.append({
|
||||||
|
"device_id": separator_device,
|
||||||
|
"action_name": "valve_open",
|
||||||
|
"action_kwargs": {
|
||||||
|
"command": "delta > 0.05"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# 估算每相的体积(假设大致平分)
|
||||||
|
phase_volume = current_volume / 2
|
||||||
|
|
||||||
|
# 智能查找分离容器底部
|
||||||
|
separation_vessel_bottom = find_separation_vessel_bottom(G, final_vessel_id) # ✅
|
||||||
|
|
||||||
|
if product_phase == "bottom":
|
||||||
|
debug_print(f"🔄 收集底相产物到 {final_to_vessel_id}")
|
||||||
|
action_sequence.append(create_action_log("收集底相产物", "📦"))
|
||||||
|
|
||||||
|
# 产物转移到目标瓶
|
||||||
|
if final_to_vessel_id:
|
||||||
|
pump_actions = generate_pump_protocol_with_rinsing(
|
||||||
|
G=G,
|
||||||
|
from_vessel=separation_vessel_bottom,
|
||||||
|
to_vessel=final_to_vessel_id,
|
||||||
|
volume=current_volume,
|
||||||
|
flowrate=2.5,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
action_sequence.extend(pump_actions)
|
||||||
|
|
||||||
|
# 放出上面那一相,60秒后关阀门
|
||||||
|
action_sequence.append({
|
||||||
|
"device_id": separator_device,
|
||||||
|
"action_name": "valve_open",
|
||||||
|
"action_kwargs": {
|
||||||
|
"command": "time > 60"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# 弃去上面那一相进废液
|
||||||
|
if final_waste_vessel_id:
|
||||||
|
pump_actions = generate_pump_protocol_with_rinsing(
|
||||||
|
G=G,
|
||||||
|
from_vessel=separation_vessel_bottom,
|
||||||
|
to_vessel=final_waste_vessel_id,
|
||||||
|
volume=current_volume,
|
||||||
|
flowrate=2.5,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
action_sequence.extend(pump_actions)
|
||||||
|
|
||||||
|
elif product_phase == "top":
|
||||||
|
debug_print(f"🔄 收集上相产物到 {final_to_vessel_id}")
|
||||||
|
action_sequence.append(create_action_log("收集上相产物", "📦"))
|
||||||
|
|
||||||
|
# 弃去下面那一相进废液
|
||||||
|
if final_waste_vessel_id:
|
||||||
|
pump_actions = generate_pump_protocol_with_rinsing(
|
||||||
|
G=G,
|
||||||
|
from_vessel=separation_vessel_bottom,
|
||||||
|
to_vessel=final_waste_vessel_id,
|
||||||
|
volume=phase_volume,
|
||||||
|
flowrate=2.5,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
action_sequence.extend(pump_actions)
|
||||||
|
|
||||||
|
# 放出上面那一相,60秒后关阀门
|
||||||
|
action_sequence.append({
|
||||||
|
"device_id": separator_device,
|
||||||
|
"action_name": "valve_open",
|
||||||
|
"action_kwargs": {
|
||||||
|
"command": "time > 60"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# 产物转移到目标瓶
|
||||||
|
if final_to_vessel_id:
|
||||||
|
pump_actions = generate_pump_protocol_with_rinsing(
|
||||||
|
G=G,
|
||||||
|
from_vessel=separation_vessel_bottom,
|
||||||
|
to_vessel=final_to_vessel_id,
|
||||||
|
volume=phase_volume,
|
||||||
|
flowrate=2.5,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
action_sequence.extend(pump_actions)
|
||||||
|
|
||||||
|
debug_print(f"✅ 分离操作已完成")
|
||||||
|
action_sequence.append(create_action_log("分离操作完成", "✅"))
|
||||||
|
|
||||||
|
# 🔧 新增:分离后体积估算
|
||||||
|
separated_volume = phase_volume * 0.95 # 假设5%损失,只保留产物相体积
|
||||||
|
update_vessel_volume(vessel, G, separated_volume, f"分离操作后(第{cycle_num}轮)")
|
||||||
|
current_volume = separated_volume
|
||||||
|
|
||||||
|
# 收集结果
|
||||||
|
if final_to_vessel_id:
|
||||||
|
action_sequence.append(
|
||||||
|
create_action_log(f"产物 ({product_phase}相) 收集到: {final_to_vessel_id}", "📦"))
|
||||||
|
if final_waste_vessel_id:
|
||||||
|
action_sequence.append(create_action_log(f"废相收集到: {final_waste_vessel_id}", "🗑️"))
|
||||||
|
|
||||||
|
else:
|
||||||
|
debug_print(f"🔄 第{cycle_num}轮 步骤4: 无分离器设备,跳过分离")
|
||||||
|
action_sequence.append(create_action_log("无分离器设备可用", "❌"))
|
||||||
|
# 添加等待时间模拟分离
|
||||||
|
action_sequence.append({
|
||||||
|
"action_name": "wait",
|
||||||
|
"action_kwargs": {"time": 10.0}
|
||||||
|
})
|
||||||
|
|
||||||
|
# 🔧 新增:如果不是最后一次,从中转瓶转移回分液漏斗(基于old版本逻辑)
|
||||||
|
if repeat_idx < repeats - 1 and final_to_vessel_id and final_to_vessel_id != final_vessel_id:
|
||||||
|
debug_print(f"🔄 第{cycle_num}轮: 产物转移回分离容器准备下一轮")
|
||||||
|
action_sequence.append(create_action_log("产物转回分离容器,准备下一轮", "🔄"))
|
||||||
|
|
||||||
|
pump_actions = generate_pump_protocol_with_rinsing(
|
||||||
|
G=G,
|
||||||
|
from_vessel=final_to_vessel_id,
|
||||||
|
to_vessel=final_vessel_id,
|
||||||
|
volume=current_volume,
|
||||||
|
flowrate=2.5,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
action_sequence.extend(pump_actions)
|
||||||
|
|
||||||
|
# 更新体积回到分离容器
|
||||||
|
update_vessel_volume(vessel, G, current_volume, f"产物转回分离容器(第{cycle_num}轮后)")
|
||||||
|
|
||||||
|
# 循环间等待(除了最后一次)
|
||||||
|
if repeat_idx < repeats - 1:
|
||||||
|
debug_print(f"🔄 第{cycle_num}轮: 等待下一次循环...")
|
||||||
|
action_sequence.append(create_action_log("等待下一次循环...", "⏳"))
|
||||||
|
action_sequence.append({
|
||||||
|
"action_name": "wait",
|
||||||
|
"action_kwargs": {"time": 5}
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
action_sequence.append(create_action_log(f"分离循环 {cycle_num}/{repeats} 完成", "🌟"))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
debug_print(f"❌ 分离工作流程执行失败: {str(e)}")
|
||||||
|
action_sequence.append(create_action_log(f"分离工作流程失败: {str(e)}", "❌"))
|
||||||
|
|
||||||
|
# 🔧 新增:分离完成后的最终状态报告
|
||||||
|
final_liquid_volume = get_vessel_liquid_volume(vessel)
|
||||||
|
|
||||||
|
# === 最终结果 ===
|
||||||
|
total_time = (stir_time + settling_time + 15) * repeats # 估算总时间
|
||||||
|
|
||||||
|
debug_print("🌀" * 20)
|
||||||
|
debug_print(f"🎉 分离协议生成完成")
|
||||||
|
debug_print(f"📊 协议统计:")
|
||||||
|
debug_print(f" 📋 总动作数: {len(action_sequence)}")
|
||||||
|
debug_print(f" ⏱️ 预计总时间: {total_time:.0f}s ({total_time / 60:.1f} 分钟)")
|
||||||
|
debug_print(f" 🥼 分离容器: {final_vessel_id}")
|
||||||
|
debug_print(f" 🎯 分离目的: {purpose}")
|
||||||
|
debug_print(f" 📊 产物相: {product_phase}")
|
||||||
|
debug_print(f" 🔄 重复次数: {repeats}")
|
||||||
|
debug_print(f"💧 体积变化统计:")
|
||||||
|
debug_print(f" - 分离前体积: {original_liquid_volume:.2f}mL")
|
||||||
|
debug_print(f" - 分离后体积: {final_liquid_volume:.2f}mL")
|
||||||
|
if solvent:
|
||||||
|
debug_print(f" 💧 溶剂: {solvent} ({final_volume}mL × {repeats}轮 = {final_volume * repeats:.2f}mL)")
|
||||||
|
if final_to_vessel_id:
|
||||||
|
debug_print(f" 🎯 产物容器: {final_to_vessel_id}")
|
||||||
|
if final_waste_vessel_id:
|
||||||
|
debug_print(f" 🗑️ 废液容器: {final_waste_vessel_id}")
|
||||||
|
debug_print("🌀" * 20)
|
||||||
|
|
||||||
|
# 添加完成日志
|
||||||
|
summary_msg = f"分离协议完成: {final_vessel_id} ({purpose},{repeats} 次循环)"
|
||||||
|
if solvent:
|
||||||
|
summary_msg += f",使用 {final_volume * repeats:.2f}mL {solvent}"
|
||||||
|
action_sequence.append(create_action_log(summary_msg, "🎉"))
|
||||||
|
|
||||||
|
return action_sequence
|
||||||
|
|
||||||
def parse_volume_input(volume_input: Union[str, float]) -> float:
|
def parse_volume_input(volume_input: Union[str, float]) -> float:
|
||||||
"""
|
"""
|
||||||
解析体积输入,支持带单位的字符串
|
解析体积输入,支持带单位的字符串
|
||||||
@@ -364,386 +820,54 @@ def update_vessel_volume(vessel: dict, G: nx.DiGraph, new_volume: float, descrip
|
|||||||
|
|
||||||
debug_print(f"📊 容器 '{vessel_id}' 体积已更新为: {new_volume:.2f}mL")
|
debug_print(f"📊 容器 '{vessel_id}' 体积已更新为: {new_volume:.2f}mL")
|
||||||
|
|
||||||
def generate_separate_protocol(
|
|
||||||
G: nx.DiGraph,
|
def find_separation_vessel_bottom(G: nx.DiGraph, vessel_id: str) -> str:
|
||||||
# 🔧 基础参数,支持XDL的vessel参数
|
|
||||||
vessel: dict = None, # 🔧 修改:从字符串改为字典类型
|
|
||||||
purpose: str = "separate", # 分离目的
|
|
||||||
product_phase: str = "top", # 产物相
|
|
||||||
# 🔧 可选的详细参数
|
|
||||||
from_vessel: Union[str, dict] = "", # 源容器(通常在separate前已经transfer了)
|
|
||||||
separation_vessel: Union[str, dict] = "", # 分离容器(与vessel同义)
|
|
||||||
to_vessel: Union[str, dict] = "", # 目标容器(可选)
|
|
||||||
waste_phase_to_vessel: Union[str, dict] = "", # 废相目标容器
|
|
||||||
product_vessel: Union[str, dict] = "", # XDL: 产物容器(与to_vessel同义)
|
|
||||||
waste_vessel: Union[str, dict] = "", # XDL: 废液容器(与waste_phase_to_vessel同义)
|
|
||||||
# 🔧 溶剂相关参数
|
|
||||||
solvent: str = "", # 溶剂名称
|
|
||||||
solvent_volume: Union[str, float] = 0.0, # 溶剂体积
|
|
||||||
volume: Union[str, float] = 0.0, # XDL: 体积(与solvent_volume同义)
|
|
||||||
# 🔧 操作参数
|
|
||||||
through: str = "", # 通过材料
|
|
||||||
repeats: int = 1, # 重复次数
|
|
||||||
stir_time: float = 30.0, # 搅拌时间(秒)
|
|
||||||
stir_speed: float = 300.0, # 搅拌速度
|
|
||||||
settling_time: float = 300.0, # 沉降时间(秒)
|
|
||||||
**kwargs
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""
|
"""
|
||||||
生成分离操作的协议序列 - 支持vessel字典和体积运算
|
智能查找分离容器的底部容器(假设为flask或vessel类型)
|
||||||
|
|
||||||
支持XDL参数格式:
|
Args:
|
||||||
- vessel: 分离容器字典(必需)
|
G: 网络图
|
||||||
- purpose: "wash", "extract", "separate"
|
vessel_id: 分离容器ID
|
||||||
- product_phase: "top", "bottom"
|
|
||||||
- product_vessel: 产物收集容器
|
|
||||||
- waste_vessel: 废液收集容器
|
|
||||||
- solvent: 溶剂名称
|
|
||||||
- volume: "200 mL", "?" 或数值
|
|
||||||
- repeats: 重复次数
|
|
||||||
|
|
||||||
分离流程:
|
Returns:
|
||||||
1. (可选)添加溶剂到分离容器
|
str: 底部容器ID
|
||||||
2. 搅拌混合
|
|
||||||
3. 静置分层
|
|
||||||
4. 收集指定相到目标容器
|
|
||||||
5. 重复指定次数
|
|
||||||
"""
|
"""
|
||||||
|
debug_print(f"🔍 查找分离容器 {vessel_id} 的底部容器...")
|
||||||
|
|
||||||
# 🔧 核心修改:vessel参数兼容处理
|
# 方法1:根据命名规则推测
|
||||||
if vessel is None:
|
possible_bottoms = [
|
||||||
if isinstance(separation_vessel, dict):
|
f"{vessel_id}_bottom",
|
||||||
vessel = separation_vessel
|
f"flask_{vessel_id}",
|
||||||
else:
|
f"vessel_{vessel_id}",
|
||||||
raise ValueError("必须提供vessel字典参数")
|
f"{vessel_id}_flask",
|
||||||
|
f"{vessel_id}_vessel"
|
||||||
|
]
|
||||||
|
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
debug_print(f"📋 尝试的底部容器名称: {possible_bottoms}")
|
||||||
# 统一处理vessel参数
|
|
||||||
if isinstance(vessel, dict):
|
|
||||||
if "id" not in vessel:
|
|
||||||
vessel_id = list(vessel.values())[0].get("id", "")
|
|
||||||
else:
|
|
||||||
vessel_id = vessel.get("id", "")
|
|
||||||
vessel_data = vessel.get("data", {})
|
|
||||||
else:
|
|
||||||
vessel_id = str(vessel)
|
|
||||||
vessel_data = G.nodes[vessel_id].get("data", {}) if vessel_id in G.nodes() else {}
|
|
||||||
|
|
||||||
debug_print("🌀" * 20)
|
for bottom_id in possible_bottoms:
|
||||||
debug_print("🚀 开始生成分离协议(支持vessel字典和体积运算)✨")
|
if bottom_id in G.nodes():
|
||||||
debug_print(f"📝 输入参数:")
|
node_type = G.nodes[bottom_id].get('type', '')
|
||||||
debug_print(f" 🥽 vessel: {vessel} (ID: {vessel_id})")
|
if node_type == 'container':
|
||||||
debug_print(f" 🎯 分离目的: '{purpose}'")
|
debug_print(f"✅ 通过命名规则找到底部容器: {bottom_id}")
|
||||||
debug_print(f" 📊 产物相: '{product_phase}'")
|
return bottom_id
|
||||||
debug_print(f" 💧 溶剂: '{solvent}'")
|
|
||||||
debug_print(f" 📏 体积: {volume} (类型: {type(volume)})")
|
|
||||||
debug_print(f" 🔄 重复次数: {repeats}")
|
|
||||||
debug_print(f" 🎯 产物容器: '{product_vessel}'")
|
|
||||||
debug_print(f" 🗑️ 废液容器: '{waste_vessel}'")
|
|
||||||
debug_print(f" 📦 其他参数: {kwargs}")
|
|
||||||
debug_print("🌀" * 20)
|
|
||||||
|
|
||||||
action_sequence = []
|
# 方法2:查找与分离器相连的容器(假设底部容器会与分离器相连)
|
||||||
|
debug_print(f"📋 方法2: 查找连接的容器...")
|
||||||
|
for node in G.nodes():
|
||||||
|
node_data = G.nodes[node]
|
||||||
|
node_class = node_data.get('class', '') or ''
|
||||||
|
|
||||||
# 🔧 新增:记录分离前的容器状态
|
if 'separator' in node_class.lower():
|
||||||
debug_print("🔍 记录分离前容器状态...")
|
# 检查分离器的输入端
|
||||||
original_liquid_volume = get_vessel_liquid_volume(vessel)
|
if G.has_edge(node, vessel_id):
|
||||||
debug_print(f"📊 分离前液体体积: {original_liquid_volume:.2f}mL")
|
for neighbor in G.neighbors(node):
|
||||||
|
if neighbor != vessel_id:
|
||||||
|
neighbor_type = G.nodes[neighbor].get('type', '')
|
||||||
|
if neighbor_type == 'container':
|
||||||
|
debug_print(f"✅ 通过连接找到底部容器: {neighbor}")
|
||||||
|
return neighbor
|
||||||
|
|
||||||
# === 参数验证和标准化 ===
|
debug_print(f"❌ 无法找到分离容器 {vessel_id} 的底部容器")
|
||||||
debug_print("🔍 步骤1: 参数验证和标准化...")
|
return ""
|
||||||
action_sequence.append(create_action_log(f"开始分离操作 - 容器: {vessel_id}", "🎬"))
|
|
||||||
action_sequence.append(create_action_log(f"分离目的: {purpose}", "🧪"))
|
|
||||||
action_sequence.append(create_action_log(f"产物相: {product_phase}", "📊"))
|
|
||||||
|
|
||||||
# 统一容器参数 - 支持字典和字符串
|
|
||||||
def extract_vessel_id(vessel_param):
|
|
||||||
if isinstance(vessel_param, dict):
|
|
||||||
return vessel_param.get("id", "")
|
|
||||||
elif isinstance(vessel_param, str):
|
|
||||||
return vessel_param
|
|
||||||
else:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
final_vessel_id = vessel_id
|
|
||||||
final_to_vessel_id = extract_vessel_id(to_vessel) or extract_vessel_id(product_vessel)
|
|
||||||
final_waste_vessel_id = extract_vessel_id(waste_phase_to_vessel) or extract_vessel_id(waste_vessel)
|
|
||||||
|
|
||||||
# 统一体积参数
|
|
||||||
final_volume = parse_volume_input(volume or solvent_volume)
|
|
||||||
|
|
||||||
# 🔧 修复:确保repeats至少为1
|
|
||||||
if repeats <= 0:
|
|
||||||
repeats = 1
|
|
||||||
debug_print(f"⚠️ 重复次数参数 <= 0,自动设置为 1")
|
|
||||||
|
|
||||||
debug_print(f"🔧 标准化后的参数:")
|
|
||||||
debug_print(f" 🥼 分离容器: '{final_vessel_id}'")
|
|
||||||
debug_print(f" 🎯 产物容器: '{final_to_vessel_id}'")
|
|
||||||
debug_print(f" 🗑️ 废液容器: '{final_waste_vessel_id}'")
|
|
||||||
debug_print(f" 📏 溶剂体积: {final_volume}mL")
|
|
||||||
debug_print(f" 🔄 重复次数: {repeats}")
|
|
||||||
|
|
||||||
action_sequence.append(create_action_log(f"分离容器: {final_vessel_id}", "🧪"))
|
|
||||||
action_sequence.append(create_action_log(f"溶剂体积: {final_volume}mL", "📏"))
|
|
||||||
action_sequence.append(create_action_log(f"重复次数: {repeats}", "🔄"))
|
|
||||||
|
|
||||||
# 验证必需参数
|
|
||||||
if not purpose:
|
|
||||||
purpose = "separate"
|
|
||||||
if not product_phase:
|
|
||||||
product_phase = "top"
|
|
||||||
if purpose not in ["wash", "extract", "separate"]:
|
|
||||||
debug_print(f"⚠️ 未知的分离目的 '{purpose}',使用默认值 'separate'")
|
|
||||||
purpose = "separate"
|
|
||||||
action_sequence.append(create_action_log(f"未知目的,使用: {purpose}", "⚠️"))
|
|
||||||
if product_phase not in ["top", "bottom"]:
|
|
||||||
debug_print(f"⚠️ 未知的产物相 '{product_phase}',使用默认值 'top'")
|
|
||||||
product_phase = "top"
|
|
||||||
action_sequence.append(create_action_log(f"未知相别,使用: {product_phase}", "⚠️"))
|
|
||||||
|
|
||||||
debug_print("✅ 参数验证通过")
|
|
||||||
action_sequence.append(create_action_log("参数验证通过", "✅"))
|
|
||||||
|
|
||||||
# === 查找设备 ===
|
|
||||||
debug_print("🔍 步骤2: 查找设备...")
|
|
||||||
action_sequence.append(create_action_log("正在查找相关设备...", "🔍"))
|
|
||||||
|
|
||||||
# 查找分离器设备
|
|
||||||
separator_device = find_separator_device(G, final_vessel_id) # 🔧 使用 final_vessel_id
|
|
||||||
if separator_device:
|
|
||||||
action_sequence.append(create_action_log(f"找到分离器设备: {separator_device}", "🧪"))
|
|
||||||
else:
|
|
||||||
debug_print("⚠️ 未找到分离器设备,可能无法执行分离")
|
|
||||||
action_sequence.append(create_action_log("未找到分离器设备", "⚠️"))
|
|
||||||
|
|
||||||
# 查找搅拌器
|
|
||||||
stirrer_device = find_connected_stirrer(G, final_vessel_id) # 🔧 使用 final_vessel_id
|
|
||||||
if stirrer_device:
|
|
||||||
action_sequence.append(create_action_log(f"找到搅拌器: {stirrer_device}", "🌪️"))
|
|
||||||
else:
|
|
||||||
action_sequence.append(create_action_log("未找到搅拌器", "⚠️"))
|
|
||||||
|
|
||||||
# 查找溶剂容器(如果需要)
|
|
||||||
solvent_vessel = ""
|
|
||||||
if solvent and solvent.strip():
|
|
||||||
solvent_vessel = find_solvent_vessel(G, solvent)
|
|
||||||
if solvent_vessel:
|
|
||||||
action_sequence.append(create_action_log(f"找到溶剂容器: {solvent_vessel}", "💧"))
|
|
||||||
else:
|
|
||||||
action_sequence.append(create_action_log(f"未找到溶剂容器: {solvent}", "⚠️"))
|
|
||||||
|
|
||||||
debug_print(f"📊 设备配置:")
|
|
||||||
debug_print(f" 🧪 分离器设备: '{separator_device}'")
|
|
||||||
debug_print(f" 🌪️ 搅拌器设备: '{stirrer_device}'")
|
|
||||||
debug_print(f" 💧 溶剂容器: '{solvent_vessel}'")
|
|
||||||
|
|
||||||
# === 执行分离流程 ===
|
|
||||||
debug_print("🔍 步骤3: 执行分离流程...")
|
|
||||||
action_sequence.append(create_action_log("开始分离工作流程", "🎯"))
|
|
||||||
|
|
||||||
# 🔧 新增:体积变化跟踪变量
|
|
||||||
current_volume = original_liquid_volume
|
|
||||||
|
|
||||||
try:
|
|
||||||
for repeat_idx in range(repeats):
|
|
||||||
cycle_num = repeat_idx + 1
|
|
||||||
debug_print(f"🔄 第{cycle_num}轮: 开始分离循环 {cycle_num}/{repeats}")
|
|
||||||
action_sequence.append(create_action_log(f"分离循环 {cycle_num}/{repeats} 开始", "🔄"))
|
|
||||||
|
|
||||||
# 步骤3.1: 添加溶剂(如果需要)
|
|
||||||
if solvent_vessel and final_volume > 0:
|
|
||||||
debug_print(f"🔄 第{cycle_num}轮 步骤1: 添加溶剂 {solvent} ({final_volume}mL)")
|
|
||||||
action_sequence.append(create_action_log(f"向分离容器添加 {final_volume}mL {solvent}", "💧"))
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 使用pump protocol添加溶剂
|
|
||||||
pump_actions = generate_pump_protocol_with_rinsing(
|
|
||||||
G=G,
|
|
||||||
from_vessel=solvent_vessel,
|
|
||||||
to_vessel=final_vessel_id, # 🔧 使用 final_vessel_id
|
|
||||||
volume=final_volume,
|
|
||||||
amount="",
|
|
||||||
time=0.0,
|
|
||||||
viscous=False,
|
|
||||||
rinsing_solvent="",
|
|
||||||
rinsing_volume=0.0,
|
|
||||||
rinsing_repeats=0,
|
|
||||||
solid=False,
|
|
||||||
flowrate=2.5,
|
|
||||||
transfer_flowrate=0.5,
|
|
||||||
rate_spec="",
|
|
||||||
event="",
|
|
||||||
through="",
|
|
||||||
**kwargs
|
|
||||||
)
|
|
||||||
action_sequence.extend(pump_actions)
|
|
||||||
debug_print(f"✅ 溶剂添加完成,添加了 {len(pump_actions)} 个动作")
|
|
||||||
action_sequence.append(create_action_log(f"溶剂转移完成 ({len(pump_actions)} 个操作)", "✅"))
|
|
||||||
|
|
||||||
# 🔧 新增:更新体积 - 添加溶剂后
|
|
||||||
current_volume += final_volume
|
|
||||||
update_vessel_volume(vessel, G, current_volume, f"添加{final_volume}mL {solvent}后")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
debug_print(f"❌ 溶剂添加失败: {str(e)}")
|
|
||||||
action_sequence.append(create_action_log(f"溶剂添加失败: {str(e)}", "❌"))
|
|
||||||
else:
|
|
||||||
debug_print(f"🔄 第{cycle_num}轮 步骤1: 无需添加溶剂")
|
|
||||||
action_sequence.append(create_action_log("无需添加溶剂", "⏭️"))
|
|
||||||
|
|
||||||
# 步骤3.2: 启动搅拌(如果有搅拌器)
|
|
||||||
if stirrer_device and stir_time > 0:
|
|
||||||
debug_print(f"🔄 第{cycle_num}轮 步骤2: 开始搅拌 ({stir_speed}rpm,持续 {stir_time}s)")
|
|
||||||
action_sequence.append(create_action_log(f"开始搅拌: {stir_speed}rpm,持续 {stir_time}s", "🌪️"))
|
|
||||||
|
|
||||||
action_sequence.append({
|
|
||||||
"device_id": stirrer_device,
|
|
||||||
"action_name": "start_stir",
|
|
||||||
"action_kwargs": {
|
|
||||||
"vessel": final_vessel_id, # 🔧 使用 final_vessel_id
|
|
||||||
"stir_speed": stir_speed,
|
|
||||||
"purpose": f"分离混合 - {purpose}"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
# 搅拌等待
|
|
||||||
stir_minutes = stir_time / 60
|
|
||||||
action_sequence.append(create_action_log(f"搅拌中,持续 {stir_minutes:.1f} 分钟", "⏱️"))
|
|
||||||
action_sequence.append({
|
|
||||||
"action_name": "wait",
|
|
||||||
"action_kwargs": {"time": stir_time}
|
|
||||||
})
|
|
||||||
|
|
||||||
# 停止搅拌
|
|
||||||
action_sequence.append(create_action_log("停止搅拌器", "🛑"))
|
|
||||||
action_sequence.append({
|
|
||||||
"device_id": stirrer_device,
|
|
||||||
"action_name": "stop_stir",
|
|
||||||
"action_kwargs": {"vessel": final_vessel_id} # 🔧 使用 final_vessel_id
|
|
||||||
})
|
|
||||||
|
|
||||||
else:
|
|
||||||
debug_print(f"🔄 第{cycle_num}轮 步骤2: 无需搅拌")
|
|
||||||
action_sequence.append(create_action_log("无需搅拌", "⏭️"))
|
|
||||||
|
|
||||||
# 步骤3.3: 静置分层
|
|
||||||
if settling_time > 0:
|
|
||||||
debug_print(f"🔄 第{cycle_num}轮 步骤3: 静置分层 ({settling_time}s)")
|
|
||||||
settling_minutes = settling_time / 60
|
|
||||||
action_sequence.append(create_action_log(f"静置分层 ({settling_minutes:.1f} 分钟)", "⚖️"))
|
|
||||||
action_sequence.append({
|
|
||||||
"action_name": "wait",
|
|
||||||
"action_kwargs": {"time": settling_time}
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
debug_print(f"🔄 第{cycle_num}轮 步骤3: 未指定静置时间")
|
|
||||||
action_sequence.append(create_action_log("未指定静置时间", "⏭️"))
|
|
||||||
|
|
||||||
# 步骤3.4: 执行分离操作
|
|
||||||
if separator_device:
|
|
||||||
debug_print(f"🔄 第{cycle_num}轮 步骤4: 执行分离操作")
|
|
||||||
action_sequence.append(create_action_log(f"执行分离: 收集{product_phase}相", "🧪"))
|
|
||||||
|
|
||||||
# 调用分离器设备的separate方法
|
|
||||||
separate_action = {
|
|
||||||
"device_id": separator_device,
|
|
||||||
"action_name": "separate",
|
|
||||||
"action_kwargs": {
|
|
||||||
"purpose": purpose,
|
|
||||||
"product_phase": product_phase,
|
|
||||||
"from_vessel": extract_vessel_id(from_vessel) or final_vessel_id, # 🔧 使用vessel_id
|
|
||||||
"separation_vessel": final_vessel_id, # 🔧 使用 final_vessel_id
|
|
||||||
"to_vessel": final_to_vessel_id or final_vessel_id, # 🔧 使用vessel_id
|
|
||||||
"waste_phase_to_vessel": final_waste_vessel_id or final_vessel_id, # 🔧 使用vessel_id
|
|
||||||
"solvent": solvent,
|
|
||||||
"solvent_volume": final_volume,
|
|
||||||
"through": through,
|
|
||||||
"repeats": 1, # 每次调用只做一次分离
|
|
||||||
"stir_time": 0, # 已经在上面完成
|
|
||||||
"stir_speed": stir_speed,
|
|
||||||
"settling_time": 0 # 已经在上面完成
|
|
||||||
}
|
|
||||||
}
|
|
||||||
action_sequence.append(separate_action)
|
|
||||||
debug_print(f"✅ 分离操作已添加")
|
|
||||||
action_sequence.append(create_action_log("分离操作完成", "✅"))
|
|
||||||
|
|
||||||
# 🔧 新增:分离后体积估算(分离通常不改变总体积,但会重新分配)
|
|
||||||
# 假设分离后保持体积(实际情况可能有少量损失)
|
|
||||||
separated_volume = current_volume * 0.95 # 假设5%损失
|
|
||||||
update_vessel_volume(vessel, G, separated_volume, f"分离操作后(第{cycle_num}轮)")
|
|
||||||
current_volume = separated_volume
|
|
||||||
|
|
||||||
# 收集结果
|
|
||||||
if final_to_vessel_id:
|
|
||||||
action_sequence.append(create_action_log(f"产物 ({product_phase}相) 收集到: {final_to_vessel_id}", "📦"))
|
|
||||||
if final_waste_vessel_id:
|
|
||||||
action_sequence.append(create_action_log(f"废相收集到: {final_waste_vessel_id}", "🗑️"))
|
|
||||||
|
|
||||||
else:
|
|
||||||
debug_print(f"🔄 第{cycle_num}轮 步骤4: 无分离器设备,跳过分离")
|
|
||||||
action_sequence.append(create_action_log("无分离器设备可用", "❌"))
|
|
||||||
# 添加等待时间模拟分离
|
|
||||||
action_sequence.append({
|
|
||||||
"action_name": "wait",
|
|
||||||
"action_kwargs": {"time": 10.0}
|
|
||||||
})
|
|
||||||
|
|
||||||
# 循环间等待(除了最后一次)
|
|
||||||
if repeat_idx < repeats - 1:
|
|
||||||
debug_print(f"🔄 第{cycle_num}轮: 等待下一次循环...")
|
|
||||||
action_sequence.append(create_action_log("等待下一次循环...", "⏳"))
|
|
||||||
action_sequence.append({
|
|
||||||
"action_name": "wait",
|
|
||||||
"action_kwargs": {"time": 5}
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
action_sequence.append(create_action_log(f"分离循环 {cycle_num}/{repeats} 完成", "🌟"))
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
debug_print(f"❌ 分离工作流程执行失败: {str(e)}")
|
|
||||||
action_sequence.append(create_action_log(f"分离工作流程失败: {str(e)}", "❌"))
|
|
||||||
# 添加错误日志
|
|
||||||
action_sequence.append({
|
|
||||||
"device_id": "system",
|
|
||||||
"action_name": "log_message",
|
|
||||||
"action_kwargs": {
|
|
||||||
"message": f"分离操作失败: {str(e)}"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
# 🔧 新增:分离完成后的最终状态报告
|
|
||||||
final_liquid_volume = get_vessel_liquid_volume(vessel)
|
|
||||||
|
|
||||||
# === 最终结果 ===
|
|
||||||
total_time = (stir_time + settling_time + 15) * repeats # 估算总时间
|
|
||||||
|
|
||||||
debug_print("🌀" * 20)
|
|
||||||
debug_print(f"🎉 分离协议生成完成")
|
|
||||||
debug_print(f"📊 协议统计:")
|
|
||||||
debug_print(f" 📋 总动作数: {len(action_sequence)}")
|
|
||||||
debug_print(f" ⏱️ 预计总时间: {total_time:.0f}s ({total_time/60:.1f} 分钟)")
|
|
||||||
debug_print(f" 🥼 分离容器: {final_vessel_id}")
|
|
||||||
debug_print(f" 🎯 分离目的: {purpose}")
|
|
||||||
debug_print(f" 📊 产物相: {product_phase}")
|
|
||||||
debug_print(f" 🔄 重复次数: {repeats}")
|
|
||||||
debug_print(f"💧 体积变化统计:")
|
|
||||||
debug_print(f" - 分离前体积: {original_liquid_volume:.2f}mL")
|
|
||||||
debug_print(f" - 分离后体积: {final_liquid_volume:.2f}mL")
|
|
||||||
if solvent:
|
|
||||||
debug_print(f" 💧 溶剂: {solvent} ({final_volume}mL × {repeats}轮 = {final_volume * repeats:.2f}mL)")
|
|
||||||
if final_to_vessel_id:
|
|
||||||
debug_print(f" 🎯 产物容器: {final_to_vessel_id}")
|
|
||||||
if final_waste_vessel_id:
|
|
||||||
debug_print(f" 🗑️ 废液容器: {final_waste_vessel_id}")
|
|
||||||
debug_print("🌀" * 20)
|
|
||||||
|
|
||||||
# 添加完成日志
|
|
||||||
summary_msg = f"分离协议完成: {final_vessel_id} ({purpose},{repeats} 次循环)"
|
|
||||||
if solvent:
|
|
||||||
summary_msg += f",使用 {final_volume * repeats:.2f}mL {solvent}"
|
|
||||||
action_sequence.append(create_action_log(summary_msg, "🎉"))
|
|
||||||
|
|
||||||
return action_sequence
|
|
||||||
|
|
||||||
|
|||||||
20
unilabos/compile/utils/vessel_parser.py
Normal file
20
unilabos/compile/utils/vessel_parser.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
def get_vessel(vessel):
|
||||||
|
"""
|
||||||
|
统一处理vessel参数,返回vessel_id和vessel_data。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
vessel: 可以是一个字典或字符串,表示vessel的ID或数据。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: 包含vessel_id和vessel_data。
|
||||||
|
"""
|
||||||
|
if isinstance(vessel, dict):
|
||||||
|
if "id" not in vessel:
|
||||||
|
vessel_id = list(vessel.values())[0].get("id", "")
|
||||||
|
else:
|
||||||
|
vessel_id = vessel.get("id", "")
|
||||||
|
vessel_data = vessel.get("data", {})
|
||||||
|
else:
|
||||||
|
vessel_id = str(vessel)
|
||||||
|
vessel_data = {}
|
||||||
|
return vessel_id, vessel_data
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
import time
|
|
||||||
import threading
|
|
||||||
|
|
||||||
|
|
||||||
class MockChiller:
|
|
||||||
def __init__(self, port: str = "MOCK"):
|
|
||||||
self.port = port
|
|
||||||
self._current_temperature: float = 25.0 # 室温开始
|
|
||||||
self._target_temperature: float = 25.0
|
|
||||||
self._status: str = "Idle"
|
|
||||||
self._is_cooling: bool = False
|
|
||||||
self._is_heating: bool = False
|
|
||||||
self._vessel = "Unknown"
|
|
||||||
self._purpose = "Unknown"
|
|
||||||
|
|
||||||
# 模拟温度变化的线程
|
|
||||||
self._temperature_thread = None
|
|
||||||
self._running = True
|
|
||||||
self._temperature_thread = threading.Thread(target=self._temperature_control_loop)
|
|
||||||
self._temperature_thread.daemon = True
|
|
||||||
self._temperature_thread.start()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def current_temperature(self) -> float:
|
|
||||||
"""当前温度 - 会被自动识别的设备属性"""
|
|
||||||
return self._current_temperature
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_temperature(self) -> float:
|
|
||||||
"""目标温度"""
|
|
||||||
return self._target_temperature
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status(self) -> str:
|
|
||||||
"""设备状态 - 会被自动识别的设备属性"""
|
|
||||||
return self._status
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_cooling(self) -> bool:
|
|
||||||
"""是否正在冷却"""
|
|
||||||
return self._is_cooling
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_heating(self) -> bool:
|
|
||||||
"""是否正在加热"""
|
|
||||||
return self._is_heating
|
|
||||||
|
|
||||||
@property
|
|
||||||
def vessel(self) -> str:
|
|
||||||
"""当前操作的容器名称"""
|
|
||||||
return self._vessel
|
|
||||||
|
|
||||||
@property
|
|
||||||
def purpose(self) -> str:
|
|
||||||
"""当前操作目的"""
|
|
||||||
return self._purpose
|
|
||||||
|
|
||||||
def heat_chill_start(self, vessel: str, temp: float, purpose: str):
|
|
||||||
"""设置目标温度并记录容器和目的"""
|
|
||||||
self._vessel = str(vessel)
|
|
||||||
self._purpose = str(purpose)
|
|
||||||
self._target_temperature = float(temp)
|
|
||||||
|
|
||||||
diff = self._target_temperature - self._current_temperature
|
|
||||||
if abs(diff) < 0.1:
|
|
||||||
self._status = "At Target Temperature"
|
|
||||||
self._is_cooling = False
|
|
||||||
self._is_heating = False
|
|
||||||
elif diff < 0:
|
|
||||||
self._status = "Cooling"
|
|
||||||
self._is_cooling = True
|
|
||||||
self._is_heating = False
|
|
||||||
else:
|
|
||||||
self._status = "Heating"
|
|
||||||
self._is_heating = True
|
|
||||||
self._is_cooling = False
|
|
||||||
|
|
||||||
self._start_temperature_control()
|
|
||||||
return True
|
|
||||||
|
|
||||||
def heat_chill_stop(self, vessel: str):
|
|
||||||
"""停止加热/制冷"""
|
|
||||||
if vessel != self._vessel:
|
|
||||||
return {"success": False, "status": f"Wrong vessel: expected {self._vessel}, got {vessel}"}
|
|
||||||
|
|
||||||
# 停止温度控制线程,锁定当前温度
|
|
||||||
self._stop_temperature_control()
|
|
||||||
|
|
||||||
# 更新状态
|
|
||||||
self._status = "Stopped"
|
|
||||||
self._is_cooling = False
|
|
||||||
self._is_heating = False
|
|
||||||
|
|
||||||
# 重新启动线程但保持温度
|
|
||||||
self._running = True
|
|
||||||
self._temperature_thread = threading.Thread(target=self._temperature_control_loop)
|
|
||||||
self._temperature_thread.daemon = True
|
|
||||||
self._temperature_thread.start()
|
|
||||||
|
|
||||||
return {"success": True, "status": self._status}
|
|
||||||
|
|
||||||
def _start_temperature_control(self):
|
|
||||||
"""启动温度控制线程"""
|
|
||||||
self._running = True
|
|
||||||
if self._temperature_thread is None or not self._temperature_thread.is_alive():
|
|
||||||
self._temperature_thread = threading.Thread(target=self._temperature_control_loop)
|
|
||||||
self._temperature_thread.daemon = True
|
|
||||||
self._temperature_thread.start()
|
|
||||||
|
|
||||||
def _stop_temperature_control(self):
|
|
||||||
"""停止温度控制"""
|
|
||||||
self._running = False
|
|
||||||
if self._temperature_thread:
|
|
||||||
self._temperature_thread.join(timeout=1.0)
|
|
||||||
|
|
||||||
def _temperature_control_loop(self):
|
|
||||||
"""温度控制循环 - 模拟真实冷却器的温度变化"""
|
|
||||||
while self._running:
|
|
||||||
# 如果状态是 Stopped,不改变温度
|
|
||||||
if self._status == "Stopped":
|
|
||||||
time.sleep(1.0)
|
|
||||||
continue
|
|
||||||
|
|
||||||
temp_diff = self._target_temperature - self._current_temperature
|
|
||||||
|
|
||||||
if abs(temp_diff) < 0.1:
|
|
||||||
self._status = "At Target Temperature"
|
|
||||||
self._is_cooling = False
|
|
||||||
self._is_heating = False
|
|
||||||
elif temp_diff < 0:
|
|
||||||
self._status = "Cooling"
|
|
||||||
self._is_cooling = True
|
|
||||||
self._is_heating = False
|
|
||||||
self._current_temperature -= 0.5
|
|
||||||
else:
|
|
||||||
self._status = "Heating"
|
|
||||||
self._is_heating = True
|
|
||||||
self._is_cooling = False
|
|
||||||
self._current_temperature += 0.3
|
|
||||||
|
|
||||||
time.sleep(1.0)
|
|
||||||
|
|
||||||
def emergency_stop(self):
|
|
||||||
"""紧急停止"""
|
|
||||||
self._status = "Emergency Stop"
|
|
||||||
self._stop_temperature_control()
|
|
||||||
self._is_cooling = False
|
|
||||||
self._is_heating = False
|
|
||||||
|
|
||||||
def get_status_info(self) -> dict:
|
|
||||||
"""获取完整状态信息"""
|
|
||||||
return {
|
|
||||||
"current_temperature": self._current_temperature,
|
|
||||||
"target_temperature": self._target_temperature,
|
|
||||||
"status": self._status,
|
|
||||||
"is_cooling": self._is_cooling,
|
|
||||||
"is_heating": self._is_heating,
|
|
||||||
"vessel": self._vessel,
|
|
||||||
"purpose": self._purpose,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# 用于测试的主函数
|
|
||||||
if __name__ == "__main__":
|
|
||||||
chiller = MockChiller()
|
|
||||||
|
|
||||||
# 测试基本功能
|
|
||||||
print("启动冷却器测试...")
|
|
||||||
print(f"初始状态: {chiller.get_status_info()}")
|
|
||||||
|
|
||||||
# 模拟运行10秒
|
|
||||||
for i in range(10):
|
|
||||||
time.sleep(1)
|
|
||||||
print(f"第{i+1}秒: 当前温度={chiller.current_temperature:.1f}°C, 状态={chiller.status}")
|
|
||||||
|
|
||||||
chiller.emergency_stop()
|
|
||||||
print("测试完成")
|
|
||||||
@@ -1,235 +0,0 @@
|
|||||||
import time
|
|
||||||
import threading
|
|
||||||
|
|
||||||
|
|
||||||
class MockFilter:
|
|
||||||
def __init__(self, port: str = "MOCK"):
|
|
||||||
# 基本参数初始化
|
|
||||||
self.port = port
|
|
||||||
self._status: str = "Idle"
|
|
||||||
self._is_filtering: bool = False
|
|
||||||
|
|
||||||
# 过滤性能参数
|
|
||||||
self._flow_rate: float = 1.0 # 流速(L/min)
|
|
||||||
self._pressure_drop: float = 0.0 # 压降(Pa)
|
|
||||||
self._filter_life: float = 100.0 # 滤芯寿命(%)
|
|
||||||
|
|
||||||
# 过滤操作参数
|
|
||||||
self._vessel: str = "" # 源容器
|
|
||||||
self._filtrate_vessel: str = "" # 目标容器
|
|
||||||
self._stir: bool = False # 是否搅拌
|
|
||||||
self._stir_speed: float = 0.0 # 搅拌速度
|
|
||||||
self._temperature: float = 25.0 # 温度(℃)
|
|
||||||
self._continue_heatchill: bool = False # 是否继续加热/制冷
|
|
||||||
self._target_volume: float = 0.0 # 目标过滤体积(L)
|
|
||||||
self._filtered_volume: float = 0.0 # 已过滤体积(L)
|
|
||||||
self._progress: float = 0.0 # 过滤进度(%)
|
|
||||||
|
|
||||||
# 线程控制
|
|
||||||
self._filter_thread = None
|
|
||||||
self._running = False
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status(self) -> str:
|
|
||||||
return self._status
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_filtering(self) -> bool:
|
|
||||||
return self._is_filtering
|
|
||||||
|
|
||||||
@property
|
|
||||||
def flow_rate(self) -> float:
|
|
||||||
return self._flow_rate
|
|
||||||
|
|
||||||
@property
|
|
||||||
def pressure_drop(self) -> float:
|
|
||||||
return self._pressure_drop
|
|
||||||
|
|
||||||
@property
|
|
||||||
def filter_life(self) -> float:
|
|
||||||
return self._filter_life
|
|
||||||
# 新增 property
|
|
||||||
@property
|
|
||||||
def vessel(self) -> str:
|
|
||||||
return self._vessel
|
|
||||||
|
|
||||||
@property
|
|
||||||
def filtrate_vessel(self) -> str:
|
|
||||||
return self._filtrate_vessel
|
|
||||||
|
|
||||||
@property
|
|
||||||
def filtered_volume(self) -> float:
|
|
||||||
return self._filtered_volume
|
|
||||||
|
|
||||||
@property
|
|
||||||
def progress(self) -> float:
|
|
||||||
return self._progress
|
|
||||||
|
|
||||||
@property
|
|
||||||
def stir(self) -> bool:
|
|
||||||
return self._stir
|
|
||||||
|
|
||||||
@property
|
|
||||||
def stir_speed(self) -> float:
|
|
||||||
return self._stir_speed
|
|
||||||
|
|
||||||
@property
|
|
||||||
def temperature(self) -> float:
|
|
||||||
return self._temperature
|
|
||||||
|
|
||||||
@property
|
|
||||||
def continue_heatchill(self) -> bool:
|
|
||||||
return self._continue_heatchill
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_volume(self) -> float:
|
|
||||||
return self._target_volume
|
|
||||||
|
|
||||||
def filter(self, vessel: str, filtrate_vessel: str, stir: bool = False, stir_speed: float = 0.0, temp: float = 25.0, continue_heatchill: bool = False, volume: float = 0.0) -> dict:
|
|
||||||
"""新的过滤操作"""
|
|
||||||
# 停止任何正在进行的过滤
|
|
||||||
if self._is_filtering:
|
|
||||||
self.stop_filtering()
|
|
||||||
# 验证参数
|
|
||||||
if volume <= 0:
|
|
||||||
return {"success": False, "message": "Target volume must be greater than 0"}
|
|
||||||
# 设置新的过滤参数
|
|
||||||
self._vessel = vessel
|
|
||||||
self._filtrate_vessel = filtrate_vessel
|
|
||||||
self._stir = stir
|
|
||||||
self._stir_speed = stir_speed
|
|
||||||
self._temperature = temp
|
|
||||||
self._continue_heatchill = continue_heatchill
|
|
||||||
self._target_volume = volume
|
|
||||||
# 重置过滤状态
|
|
||||||
self._filtered_volume = 0.0
|
|
||||||
self._progress = 0.0
|
|
||||||
self._status = "Starting Filter"
|
|
||||||
# 启动过滤过程
|
|
||||||
self._flow_rate = 1.0 # 设置默认流速
|
|
||||||
self._start_filter_process()
|
|
||||||
|
|
||||||
return {"success": True, "message": "Filter started"}
|
|
||||||
|
|
||||||
def stop_filtering(self):
|
|
||||||
"""停止过滤"""
|
|
||||||
self._status = "Stopping Filter"
|
|
||||||
self._stop_filter_process()
|
|
||||||
self._flow_rate = 0.0
|
|
||||||
self._is_filtering = False
|
|
||||||
self._status = "Stopped"
|
|
||||||
return True
|
|
||||||
|
|
||||||
def replace_filter(self):
|
|
||||||
"""更换滤芯"""
|
|
||||||
self._filter_life = 100.0
|
|
||||||
self._status = "Filter Replaced"
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _start_filter_process(self):
|
|
||||||
"""启动过滤过程线程"""
|
|
||||||
if not self._running:
|
|
||||||
self._running = True
|
|
||||||
self._is_filtering = True
|
|
||||||
self._filter_thread = threading.Thread(target=self._filter_loop)
|
|
||||||
self._filter_thread.daemon = True
|
|
||||||
self._filter_thread.start()
|
|
||||||
|
|
||||||
def _stop_filter_process(self):
|
|
||||||
"""停止过滤过程"""
|
|
||||||
self._running = False
|
|
||||||
if self._filter_thread:
|
|
||||||
self._filter_thread.join(timeout=1.0)
|
|
||||||
|
|
||||||
def _filter_loop(self):
|
|
||||||
"""过滤进程主循环"""
|
|
||||||
update_interval = 1.0 # 更新间隔(秒)
|
|
||||||
|
|
||||||
while self._running and self._is_filtering:
|
|
||||||
try:
|
|
||||||
self._status = "Filtering"
|
|
||||||
|
|
||||||
# 计算这一秒过滤的体积 (L/min -> L/s)
|
|
||||||
volume_increment = (self._flow_rate / 60.0) * update_interval
|
|
||||||
|
|
||||||
# 更新已过滤体积
|
|
||||||
self._filtered_volume += volume_increment
|
|
||||||
|
|
||||||
# 更新进度 (避免除零错误)
|
|
||||||
if self._target_volume > 0:
|
|
||||||
self._progress = min(100.0, (self._filtered_volume / self._target_volume) * 100.0)
|
|
||||||
|
|
||||||
# 更新滤芯寿命 (每过滤1L减少0.5%寿命)
|
|
||||||
self._filter_life = max(0.0, self._filter_life - (volume_increment * 0.5))
|
|
||||||
|
|
||||||
# 更新压降 (根据滤芯寿命和流速动态计算)
|
|
||||||
life_factor = self._filter_life / 100.0 # 将寿命转换为0-1的因子
|
|
||||||
flow_factor = self._flow_rate / 2.0 # 将流速标准化(假设2L/min是标准流速)
|
|
||||||
base_pressure = 100.0 # 基础压降
|
|
||||||
# 压降随滤芯寿命降低而增加,随流速增加而增加
|
|
||||||
self._pressure_drop = base_pressure * (2 - life_factor) * flow_factor
|
|
||||||
|
|
||||||
# 检查是否完成目标体积
|
|
||||||
if self._target_volume > 0 and self._filtered_volume >= self._target_volume:
|
|
||||||
self._status = "Completed"
|
|
||||||
self._progress = 100.0
|
|
||||||
self.stop_filtering()
|
|
||||||
break
|
|
||||||
|
|
||||||
# 检查滤芯寿命
|
|
||||||
if self._filter_life <= 10.0:
|
|
||||||
self._status = "Filter Needs Replacement"
|
|
||||||
|
|
||||||
time.sleep(update_interval)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error in filter loop: {e}")
|
|
||||||
self.emergency_stop()
|
|
||||||
break
|
|
||||||
|
|
||||||
def emergency_stop(self):
|
|
||||||
"""紧急停止"""
|
|
||||||
self._status = "Emergency Stop"
|
|
||||||
self._stop_filter_process()
|
|
||||||
self._is_filtering = False
|
|
||||||
self._flow_rate = 0.0
|
|
||||||
|
|
||||||
def get_status_info(self) -> dict:
|
|
||||||
"""扩展的状态信息"""
|
|
||||||
return {
|
|
||||||
"status": self._status,
|
|
||||||
"is_filtering": self._is_filtering,
|
|
||||||
"flow_rate": self._flow_rate,
|
|
||||||
"pressure_drop": self._pressure_drop,
|
|
||||||
"filter_life": self._filter_life,
|
|
||||||
"vessel": self._vessel,
|
|
||||||
"filtrate_vessel": self._filtrate_vessel,
|
|
||||||
"filtered_volume": self._filtered_volume,
|
|
||||||
"target_volume": self._target_volume,
|
|
||||||
"progress": self._progress,
|
|
||||||
"temperature": self._temperature,
|
|
||||||
"stir": self._stir,
|
|
||||||
"stir_speed": self._stir_speed
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# 用于测试的主函数
|
|
||||||
if __name__ == "__main__":
|
|
||||||
filter_device = MockFilter()
|
|
||||||
|
|
||||||
# 测试基本功能
|
|
||||||
print("启动过滤器测试...")
|
|
||||||
print(f"初始状态: {filter_device.get_status_info()}")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 模拟运行10秒
|
|
||||||
for i in range(10):
|
|
||||||
time.sleep(1)
|
|
||||||
print(
|
|
||||||
f"第{i+1}秒: "
|
|
||||||
f"寿命={filter_device.filter_life:.1f}%, 状态={filter_device.status}"
|
|
||||||
)
|
|
||||||
|
|
||||||
filter_device.emergency_stop()
|
|
||||||
print("测试完成")
|
|
||||||
@@ -1,247 +0,0 @@
|
|||||||
import time
|
|
||||||
import threading
|
|
||||||
|
|
||||||
class MockHeater:
|
|
||||||
def __init__(self, port: str = "MOCK"):
|
|
||||||
self.port = port
|
|
||||||
self._current_temperature: float = 25.0 # 室温开始
|
|
||||||
self._target_temperature: float = 25.0
|
|
||||||
self._status: str = "Idle"
|
|
||||||
self._is_heating: bool = False
|
|
||||||
self._heating_power: float = 0.0 # 加热功率百分比 0-100
|
|
||||||
self._max_temperature: float = 300.0 # 最大加热温度
|
|
||||||
|
|
||||||
# 新增加的属性
|
|
||||||
self._vessel: str = "Unknown"
|
|
||||||
self._purpose: str = "Unknown"
|
|
||||||
self._stir: bool = False
|
|
||||||
self._stir_speed: float = 0.0
|
|
||||||
|
|
||||||
# 模拟加热过程的线程
|
|
||||||
self._heating_thread = None
|
|
||||||
self._running = True
|
|
||||||
self._heating_thread = threading.Thread(target=self._heating_control_loop)
|
|
||||||
self._heating_thread.daemon = True
|
|
||||||
self._heating_thread.start()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def current_temperature(self) -> float:
|
|
||||||
"""当前温度 - 会被自动识别的设备属性"""
|
|
||||||
return self._current_temperature
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_temperature(self) -> float:
|
|
||||||
"""目标温度"""
|
|
||||||
return self._target_temperature
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status(self) -> str:
|
|
||||||
"""设备状态 - 会被自动识别的设备属性"""
|
|
||||||
return self._status
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_heating(self) -> bool:
|
|
||||||
"""是否正在加热"""
|
|
||||||
return self._is_heating
|
|
||||||
|
|
||||||
@property
|
|
||||||
def heating_power(self) -> float:
|
|
||||||
"""加热功率百分比"""
|
|
||||||
return self._heating_power
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_temperature(self) -> float:
|
|
||||||
"""最大加热温度"""
|
|
||||||
return self._max_temperature
|
|
||||||
|
|
||||||
@property
|
|
||||||
def vessel(self) -> str:
|
|
||||||
"""当前操作的容器名称"""
|
|
||||||
return self._vessel
|
|
||||||
|
|
||||||
@property
|
|
||||||
def purpose(self) -> str:
|
|
||||||
"""操作目的"""
|
|
||||||
return self._purpose
|
|
||||||
|
|
||||||
@property
|
|
||||||
def stir(self) -> bool:
|
|
||||||
"""是否搅拌"""
|
|
||||||
return self._stir
|
|
||||||
|
|
||||||
@property
|
|
||||||
def stir_speed(self) -> float:
|
|
||||||
"""搅拌速度"""
|
|
||||||
return self._stir_speed
|
|
||||||
|
|
||||||
def heat_chill_start(self, vessel: str, temp: float, purpose: str) -> dict:
|
|
||||||
"""开始加热/制冷过程"""
|
|
||||||
self._vessel = str(vessel)
|
|
||||||
self._purpose = str(purpose)
|
|
||||||
self._target_temperature = float(temp)
|
|
||||||
|
|
||||||
diff = self._target_temperature - self._current_temperature
|
|
||||||
if abs(diff) < 0.1:
|
|
||||||
self._status = "At Target Temperature"
|
|
||||||
self._is_heating = False
|
|
||||||
elif diff > 0:
|
|
||||||
self._status = "Heating"
|
|
||||||
self._is_heating = True
|
|
||||||
else:
|
|
||||||
self._status = "Cooling Down"
|
|
||||||
self._is_heating = False
|
|
||||||
|
|
||||||
return {"success": True, "status": self._status}
|
|
||||||
|
|
||||||
def heat_chill_stop(self, vessel: str) -> dict:
|
|
||||||
"""停止加热/制冷"""
|
|
||||||
if vessel != self._vessel:
|
|
||||||
return {"success": False, "status": f"Wrong vessel: expected {self._vessel}, got {vessel}"}
|
|
||||||
|
|
||||||
self._status = "Stopped"
|
|
||||||
self._is_heating = False
|
|
||||||
self._heating_power = 0.0
|
|
||||||
|
|
||||||
return {"success": True, "status": self._status}
|
|
||||||
|
|
||||||
def heat_chill(self, vessel: str, temp: float, time: float,
|
|
||||||
stir: bool = False, stir_speed: float = 0.0,
|
|
||||||
purpose: str = "Unknown") -> dict:
|
|
||||||
"""完整的加热/制冷控制"""
|
|
||||||
self._vessel = str(vessel)
|
|
||||||
self._target_temperature = float(temp)
|
|
||||||
self._purpose = str(purpose)
|
|
||||||
self._stir = stir
|
|
||||||
self._stir_speed = stir_speed
|
|
||||||
|
|
||||||
diff = self._target_temperature - self._current_temperature
|
|
||||||
if abs(diff) < 0.1:
|
|
||||||
self._status = "At Target Temperature"
|
|
||||||
self._is_heating = False
|
|
||||||
elif diff > 0:
|
|
||||||
self._status = "Heating"
|
|
||||||
self._is_heating = True
|
|
||||||
else:
|
|
||||||
self._status = "Cooling Down"
|
|
||||||
self._is_heating = False
|
|
||||||
|
|
||||||
return {"success": True, "status": self._status}
|
|
||||||
|
|
||||||
def set_temperature(self, temperature: float):
|
|
||||||
"""设置目标温度 - 需要在注册表添加的设备动作"""
|
|
||||||
try:
|
|
||||||
temperature = float(temperature)
|
|
||||||
except ValueError:
|
|
||||||
self._status = "Error: Invalid temperature value"
|
|
||||||
return False
|
|
||||||
|
|
||||||
if temperature > self._max_temperature:
|
|
||||||
self._status = f"Error: Temperature exceeds maximum ({self._max_temperature}°C)"
|
|
||||||
return False
|
|
||||||
|
|
||||||
self._target_temperature = temperature
|
|
||||||
self._status = "Setting Temperature"
|
|
||||||
|
|
||||||
# 启动加热控制
|
|
||||||
self._start_heating_control()
|
|
||||||
return True
|
|
||||||
|
|
||||||
def set_heating_power(self, power: float):
|
|
||||||
"""设置加热功率"""
|
|
||||||
try:
|
|
||||||
power = float(power)
|
|
||||||
except ValueError:
|
|
||||||
self._status = "Error: Invalid power value"
|
|
||||||
return False
|
|
||||||
|
|
||||||
self._heating_power = max(0.0, min(100.0, power)) # 限制在0-100%
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _start_heating_control(self):
|
|
||||||
"""启动加热控制线程"""
|
|
||||||
if not self._running:
|
|
||||||
self._running = True
|
|
||||||
self._heating_thread = threading.Thread(target=self._heating_control_loop)
|
|
||||||
self._heating_thread.daemon = True
|
|
||||||
self._heating_thread.start()
|
|
||||||
|
|
||||||
def _stop_heating_control(self):
|
|
||||||
"""停止加热控制"""
|
|
||||||
self._running = False
|
|
||||||
if self._heating_thread:
|
|
||||||
self._heating_thread.join(timeout=1.0)
|
|
||||||
|
|
||||||
def _heating_control_loop(self):
|
|
||||||
"""加热控制循环"""
|
|
||||||
while self._running:
|
|
||||||
# 如果状态是 Stopped,不改变温度
|
|
||||||
if self._status == "Stopped":
|
|
||||||
time.sleep(1.0)
|
|
||||||
continue
|
|
||||||
|
|
||||||
temp_diff = self._target_temperature - self._current_temperature
|
|
||||||
|
|
||||||
if abs(temp_diff) < 0.1:
|
|
||||||
self._status = "At Target Temperature"
|
|
||||||
self._is_heating = False
|
|
||||||
self._heating_power = 10.0
|
|
||||||
elif temp_diff > 0:
|
|
||||||
self._status = "Heating"
|
|
||||||
self._is_heating = True
|
|
||||||
self._heating_power = min(100.0, abs(temp_diff) * 2)
|
|
||||||
self._current_temperature += 0.5
|
|
||||||
else:
|
|
||||||
self._status = "Cooling Down"
|
|
||||||
self._is_heating = False
|
|
||||||
self._heating_power = 0.0
|
|
||||||
self._current_temperature -= 0.2
|
|
||||||
|
|
||||||
time.sleep(1.0)
|
|
||||||
|
|
||||||
def emergency_stop(self):
|
|
||||||
"""紧急停止"""
|
|
||||||
self._status = "Emergency Stop"
|
|
||||||
self._stop_heating_control()
|
|
||||||
self._is_heating = False
|
|
||||||
self._heating_power = 0.0
|
|
||||||
|
|
||||||
def get_status_info(self) -> dict:
|
|
||||||
"""获取完整状态信息"""
|
|
||||||
return {
|
|
||||||
"current_temperature": self._current_temperature,
|
|
||||||
"target_temperature": self._target_temperature,
|
|
||||||
"status": self._status,
|
|
||||||
"is_heating": self._is_heating,
|
|
||||||
"heating_power": self._heating_power,
|
|
||||||
"max_temperature": self._max_temperature,
|
|
||||||
"vessel": self._vessel,
|
|
||||||
"purpose": self._purpose,
|
|
||||||
"stir": self._stir,
|
|
||||||
"stir_speed": self._stir_speed
|
|
||||||
}
|
|
||||||
|
|
||||||
# 用于测试的主函数
|
|
||||||
if __name__ == "__main__":
|
|
||||||
heater = MockHeater()
|
|
||||||
|
|
||||||
print("启动加热器测试...")
|
|
||||||
print(f"初始状态: {heater.get_status_info()}")
|
|
||||||
|
|
||||||
# 设置目标温度为80度
|
|
||||||
heater.set_temperature(80.0)
|
|
||||||
|
|
||||||
# 模拟运行15秒
|
|
||||||
try:
|
|
||||||
for i in range(15):
|
|
||||||
time.sleep(1)
|
|
||||||
status = heater.get_status_info()
|
|
||||||
print(
|
|
||||||
f"\r温度: {status['current_temperature']:.1f}°C / {status['target_temperature']:.1f}°C | "
|
|
||||||
f"功率: {status['heating_power']:.1f}% | 状态: {status['status']}",
|
|
||||||
end=""
|
|
||||||
)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
heater.emergency_stop()
|
|
||||||
print("\n测试被手动停止")
|
|
||||||
|
|
||||||
print("\n测试完成")
|
|
||||||
@@ -1,360 +0,0 @@
|
|||||||
import time
|
|
||||||
import threading
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
|
|
||||||
class MockPump:
|
|
||||||
def __init__(self, port: str = "MOCK"):
|
|
||||||
self.port = port
|
|
||||||
|
|
||||||
# 设备基本状态属性
|
|
||||||
self._current_device = "MockPump1" # 设备标识符
|
|
||||||
self._status: str = "Idle" # 设备状态:Idle, Running, Error, Stopped
|
|
||||||
self._pump_state: str = "Stopped" # 泵运行状态:Running, Stopped, Paused
|
|
||||||
|
|
||||||
# 流量相关属性
|
|
||||||
self._flow_rate: float = 0.0 # 当前流速 (mL/min)
|
|
||||||
self._target_flow_rate: float = 0.0 # 目标流速 (mL/min)
|
|
||||||
self._max_flow_rate: float = 100.0 # 最大流速 (mL/min)
|
|
||||||
self._total_volume: float = 0.0 # 累计流量 (mL)
|
|
||||||
|
|
||||||
# 压力相关属性
|
|
||||||
self._pressure: float = 0.0 # 当前压力 (bar)
|
|
||||||
self._max_pressure: float = 10.0 # 最大压力 (bar)
|
|
||||||
|
|
||||||
# 运行控制线程
|
|
||||||
self._pump_thread = None
|
|
||||||
self._running = False
|
|
||||||
self._thread_lock = threading.Lock()
|
|
||||||
|
|
||||||
# 新增 PumpTransfer 相关属性
|
|
||||||
self._from_vessel: str = ""
|
|
||||||
self._to_vessel: str = ""
|
|
||||||
self._transfer_volume: float = 0.0
|
|
||||||
self._amount: str = ""
|
|
||||||
self._transfer_time: float = 0.0
|
|
||||||
self._is_viscous: bool = False
|
|
||||||
self._rinsing_solvent: str = ""
|
|
||||||
self._rinsing_volume: float = 0.0
|
|
||||||
self._rinsing_repeats: int = 0
|
|
||||||
self._is_solid: bool = False
|
|
||||||
|
|
||||||
# 时间追踪
|
|
||||||
self._start_time: datetime = None
|
|
||||||
self._time_spent: timedelta = timedelta()
|
|
||||||
self._time_remaining: timedelta = timedelta()
|
|
||||||
|
|
||||||
# ==================== 状态属性 ====================
|
|
||||||
# 这些属性会被Uni-Lab系统自动识别并定时对外广播
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status(self) -> str:
|
|
||||||
return self._status
|
|
||||||
|
|
||||||
@property
|
|
||||||
def current_device(self) -> str:
|
|
||||||
"""当前设备标识符"""
|
|
||||||
return self._current_device
|
|
||||||
|
|
||||||
@property
|
|
||||||
def pump_state(self) -> str:
|
|
||||||
return self._pump_state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def flow_rate(self) -> float:
|
|
||||||
return self._flow_rate
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_flow_rate(self) -> float:
|
|
||||||
return self._target_flow_rate
|
|
||||||
|
|
||||||
@property
|
|
||||||
def pressure(self) -> float:
|
|
||||||
return self._pressure
|
|
||||||
|
|
||||||
@property
|
|
||||||
def total_volume(self) -> float:
|
|
||||||
return self._total_volume
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_flow_rate(self) -> float:
|
|
||||||
return self._max_flow_rate
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_pressure(self) -> float:
|
|
||||||
return self._max_pressure
|
|
||||||
|
|
||||||
# 添加新的属性访问器
|
|
||||||
@property
|
|
||||||
def from_vessel(self) -> str:
|
|
||||||
return self._from_vessel
|
|
||||||
|
|
||||||
@property
|
|
||||||
def to_vessel(self) -> str:
|
|
||||||
return self._to_vessel
|
|
||||||
|
|
||||||
@property
|
|
||||||
def transfer_volume(self) -> float:
|
|
||||||
return self._transfer_volume
|
|
||||||
|
|
||||||
@property
|
|
||||||
def amount(self) -> str:
|
|
||||||
return self._amount
|
|
||||||
|
|
||||||
@property
|
|
||||||
def transfer_time(self) -> float:
|
|
||||||
return self._transfer_time
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_viscous(self) -> bool:
|
|
||||||
return self._is_viscous
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rinsing_solvent(self) -> str:
|
|
||||||
return self._rinsing_solvent
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rinsing_volume(self) -> float:
|
|
||||||
return self._rinsing_volume
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rinsing_repeats(self) -> int:
|
|
||||||
return self._rinsing_repeats
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_solid(self) -> bool:
|
|
||||||
return self._is_solid
|
|
||||||
|
|
||||||
# 修改这两个属性装饰器
|
|
||||||
@property
|
|
||||||
def time_spent(self) -> float:
|
|
||||||
"""已用时间(秒)"""
|
|
||||||
if isinstance(self._time_spent, timedelta):
|
|
||||||
return self._time_spent.total_seconds()
|
|
||||||
return float(self._time_spent)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def time_remaining(self) -> float:
|
|
||||||
"""剩余时间(秒)"""
|
|
||||||
if isinstance(self._time_remaining, timedelta):
|
|
||||||
return self._time_remaining.total_seconds()
|
|
||||||
return float(self._time_remaining)
|
|
||||||
|
|
||||||
# ==================== 设备控制方法 ====================
|
|
||||||
# 这些方法需要在注册表中添加,会作为ActionServer接受控制指令
|
|
||||||
def pump_transfer(self, from_vessel: str, to_vessel: str, volume: float,
|
|
||||||
amount: str = "", time: float = 0.0, viscous: bool = False,
|
|
||||||
rinsing_solvent: str = "", rinsing_volume: float = 0.0,
|
|
||||||
rinsing_repeats: int = 0, solid: bool = False) -> dict:
|
|
||||||
"""Execute pump transfer operation"""
|
|
||||||
# Stop any existing operation first
|
|
||||||
self._stop_pump_operation()
|
|
||||||
|
|
||||||
# Set transfer parameters
|
|
||||||
self._from_vessel = from_vessel
|
|
||||||
self._to_vessel = to_vessel
|
|
||||||
self._transfer_volume = float(volume)
|
|
||||||
self._amount = amount
|
|
||||||
self._transfer_time = float(time)
|
|
||||||
self._is_viscous = viscous
|
|
||||||
self._rinsing_solvent = rinsing_solvent
|
|
||||||
self._rinsing_volume = float(rinsing_volume)
|
|
||||||
self._rinsing_repeats = int(rinsing_repeats)
|
|
||||||
self._is_solid = solid
|
|
||||||
|
|
||||||
# Calculate flow rate
|
|
||||||
if self._transfer_time > 0 and self._transfer_volume > 0:
|
|
||||||
self._target_flow_rate = (self._transfer_volume / self._transfer_time) * 60.0
|
|
||||||
else:
|
|
||||||
self._target_flow_rate = 10.0 if not self._is_viscous else 5.0
|
|
||||||
|
|
||||||
# Reset timers and counters
|
|
||||||
self._start_time = datetime.now()
|
|
||||||
self._time_spent = timedelta()
|
|
||||||
self._time_remaining = timedelta(seconds=self._transfer_time)
|
|
||||||
self._total_volume = 0.0
|
|
||||||
self._flow_rate = 0.0
|
|
||||||
|
|
||||||
# Start pump operation
|
|
||||||
self._pump_state = "Running"
|
|
||||||
self._status = "Starting Transfer"
|
|
||||||
self._running = True
|
|
||||||
|
|
||||||
# Start pump operation thread
|
|
||||||
self._pump_thread = threading.Thread(target=self._pump_operation_loop)
|
|
||||||
self._pump_thread.daemon = True
|
|
||||||
self._pump_thread.start()
|
|
||||||
|
|
||||||
# Wait briefly to ensure thread starts
|
|
||||||
time.sleep(0.1)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"status": self._status,
|
|
||||||
"current_device": self._current_device,
|
|
||||||
"time_spent": 0.0,
|
|
||||||
"time_remaining": float(self._transfer_time)
|
|
||||||
}
|
|
||||||
|
|
||||||
def pause_pump(self) -> str:
|
|
||||||
|
|
||||||
if self._pump_state != "Running":
|
|
||||||
self._status = "Error: Pump not running"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._pump_state = "Paused"
|
|
||||||
self._status = "Pump Paused"
|
|
||||||
self._stop_pump_operation()
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def resume_pump(self) -> str:
|
|
||||||
|
|
||||||
if self._pump_state != "Paused":
|
|
||||||
self._status = "Error: Pump not paused"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._pump_state = "Running"
|
|
||||||
self._status = "Resuming Pump"
|
|
||||||
self._start_pump_operation()
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def reset_volume_counter(self) -> str:
|
|
||||||
self._total_volume = 0.0
|
|
||||||
self._status = "Volume counter reset"
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def emergency_stop(self) -> str:
|
|
||||||
self._status = "Emergency Stop"
|
|
||||||
self._pump_state = "Stopped"
|
|
||||||
self._stop_pump_operation()
|
|
||||||
self._flow_rate = 0.0
|
|
||||||
self._pressure = 0.0
|
|
||||||
self._target_flow_rate = 0.0
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
# ==================== 内部控制方法 ====================
|
|
||||||
|
|
||||||
def _start_pump_operation(self):
|
|
||||||
with self._thread_lock:
|
|
||||||
if not self._running:
|
|
||||||
self._running = True
|
|
||||||
self._pump_thread = threading.Thread(target=self._pump_operation_loop)
|
|
||||||
self._pump_thread.daemon = True
|
|
||||||
self._pump_thread.start()
|
|
||||||
|
|
||||||
def _stop_pump_operation(self):
|
|
||||||
with self._thread_lock:
|
|
||||||
self._running = False
|
|
||||||
if self._pump_thread and self._pump_thread.is_alive():
|
|
||||||
self._pump_thread.join(timeout=2.0)
|
|
||||||
|
|
||||||
def _pump_operation_loop(self):
|
|
||||||
"""泵运行主循环"""
|
|
||||||
print("Pump operation loop started") # Debug print
|
|
||||||
|
|
||||||
while self._running and self._pump_state == "Running":
|
|
||||||
try:
|
|
||||||
# Calculate flow rate adjustment
|
|
||||||
flow_diff = self._target_flow_rate - self._flow_rate
|
|
||||||
|
|
||||||
# Adjust flow rate more aggressively (50% of difference)
|
|
||||||
adjustment = flow_diff * 0.5
|
|
||||||
self._flow_rate += adjustment
|
|
||||||
|
|
||||||
# Ensure flow rate is within bounds
|
|
||||||
self._flow_rate = max(0.1, min(self._max_flow_rate, self._flow_rate))
|
|
||||||
|
|
||||||
# Update status based on flow rate
|
|
||||||
if abs(flow_diff) < 0.1:
|
|
||||||
self._status = "Running at Target Flow Rate"
|
|
||||||
else:
|
|
||||||
self._status = "Adjusting Flow Rate"
|
|
||||||
|
|
||||||
# Calculate volume increment
|
|
||||||
volume_increment = (self._flow_rate / 60.0) # mL/s
|
|
||||||
self._total_volume += volume_increment
|
|
||||||
|
|
||||||
# Update time tracking
|
|
||||||
self._time_spent = datetime.now() - self._start_time
|
|
||||||
if self._transfer_time > 0:
|
|
||||||
remaining = self._transfer_time - self._time_spent.total_seconds()
|
|
||||||
self._time_remaining = timedelta(seconds=max(0, remaining))
|
|
||||||
|
|
||||||
# Check completion
|
|
||||||
if self._total_volume >= self._transfer_volume:
|
|
||||||
self._status = "Transfer Completed"
|
|
||||||
self._pump_state = "Stopped"
|
|
||||||
self._running = False
|
|
||||||
break
|
|
||||||
|
|
||||||
# Update pressure
|
|
||||||
self._pressure = (self._flow_rate / self._max_flow_rate) * self._max_pressure
|
|
||||||
|
|
||||||
print(f"Debug - Flow: {self._flow_rate:.1f}, Volume: {self._total_volume:.1f}") # Debug print
|
|
||||||
|
|
||||||
time.sleep(1.0)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error in pump operation: {str(e)}")
|
|
||||||
self._status = "Error in pump operation"
|
|
||||||
self._pump_state = "Stopped"
|
|
||||||
self._running = False
|
|
||||||
break
|
|
||||||
|
|
||||||
def get_status_info(self) -> dict:
|
|
||||||
"""
|
|
||||||
获取完整的设备状态信息
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: 包含所有设备状态的字典
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
"status": self._status,
|
|
||||||
"pump_state": self._pump_state,
|
|
||||||
"flow_rate": self._flow_rate,
|
|
||||||
"target_flow_rate": self._target_flow_rate,
|
|
||||||
"pressure": self._pressure,
|
|
||||||
"total_volume": self._total_volume,
|
|
||||||
"max_flow_rate": self._max_flow_rate,
|
|
||||||
"max_pressure": self._max_pressure,
|
|
||||||
"current_device": self._current_device,
|
|
||||||
"from_vessel": self._from_vessel,
|
|
||||||
"to_vessel": self._to_vessel,
|
|
||||||
"transfer_volume": self._transfer_volume,
|
|
||||||
"amount": self._amount,
|
|
||||||
"transfer_time": self._transfer_time,
|
|
||||||
"is_viscous": self._is_viscous,
|
|
||||||
"rinsing_solvent": self._rinsing_solvent,
|
|
||||||
"rinsing_volume": self._rinsing_volume,
|
|
||||||
"rinsing_repeats": self._rinsing_repeats,
|
|
||||||
"is_solid": self._is_solid,
|
|
||||||
"time_spent": self._time_spent.total_seconds(),
|
|
||||||
"time_remaining": self._time_remaining.total_seconds()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# 用于测试的主函数
|
|
||||||
if __name__ == "__main__":
|
|
||||||
pump = MockPump()
|
|
||||||
|
|
||||||
# 测试基本功能
|
|
||||||
print("启动泵设备测试...")
|
|
||||||
print(f"初始状态: {pump.get_status_info()}")
|
|
||||||
|
|
||||||
# 设置流速并启动
|
|
||||||
pump.set_flow_rate(50.0)
|
|
||||||
pump.start_pump()
|
|
||||||
|
|
||||||
# 模拟运行10秒
|
|
||||||
for i in range(10):
|
|
||||||
time.sleep(1)
|
|
||||||
print(f"第{i+1}秒: 流速={pump.flow_rate:.1f}mL/min, 压力={pump.pressure:.2f}bar, 状态={pump.status}")
|
|
||||||
|
|
||||||
# 测试方向切换
|
|
||||||
print("切换泵方向...")
|
|
||||||
|
|
||||||
|
|
||||||
pump.emergency_stop()
|
|
||||||
print("测试完成")
|
|
||||||
@@ -1,390 +0,0 @@
|
|||||||
import time
|
|
||||||
import threading
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
class MockRotavap:
|
|
||||||
"""
|
|
||||||
模拟旋转蒸发器设备类
|
|
||||||
|
|
||||||
这个类模拟了一个实验室旋转蒸发器的行为,包括旋转控制、
|
|
||||||
真空泵控制、温度控制等功能。参考了现有的 RotavapOne 实现。
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, port: str = "MOCK"):
|
|
||||||
"""
|
|
||||||
初始化MockRotavap实例
|
|
||||||
|
|
||||||
Args:
|
|
||||||
port (str): 设备端口,默认为"MOCK"表示模拟设备
|
|
||||||
"""
|
|
||||||
self.port = port
|
|
||||||
|
|
||||||
# 设备基本状态属性
|
|
||||||
self._status: str = "Idle" # 设备状态:Idle, Running, Error, Stopped
|
|
||||||
|
|
||||||
# 旋转相关属性
|
|
||||||
self._rotate_state: str = "Stopped" # 旋转状态:Running, Stopped
|
|
||||||
self._rotate_time: float = 0.0 # 旋转剩余时间 (秒)
|
|
||||||
self._rotate_speed: float = 0.0 # 旋转速度 (rpm)
|
|
||||||
self._max_rotate_speed: float = 300.0 # 最大旋转速度 (rpm)
|
|
||||||
|
|
||||||
# 真空泵相关属性
|
|
||||||
self._pump_state: str = "Stopped" # 泵状态:Running, Stopped
|
|
||||||
self._pump_time: float = 0.0 # 泵剩余时间 (秒)
|
|
||||||
self._vacuum_level: float = 0.0 # 真空度 (mbar)
|
|
||||||
self._target_vacuum: float = 50.0 # 目标真空度 (mbar)
|
|
||||||
|
|
||||||
# 温度相关属性
|
|
||||||
self._temperature: float = 25.0 # 水浴温度 (°C)
|
|
||||||
self._target_temperature: float = 25.0 # 目标温度 (°C)
|
|
||||||
self._max_temperature: float = 180.0 # 最大温度 (°C)
|
|
||||||
|
|
||||||
# 运行控制线程
|
|
||||||
self._operation_thread = None
|
|
||||||
self._running = False
|
|
||||||
self._thread_lock = threading.Lock()
|
|
||||||
|
|
||||||
# 操作成功标志
|
|
||||||
self.success: str = "True" # 使用字符串而不是布尔值
|
|
||||||
|
|
||||||
# ==================== 状态属性 ====================
|
|
||||||
# 这些属性会被Uni-Lab系统自动识别并定时对外广播
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status(self) -> str:
|
|
||||||
return self._status
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rotate_state(self) -> str:
|
|
||||||
return self._rotate_state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rotate_time(self) -> float:
|
|
||||||
return self._rotate_time
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rotate_speed(self) -> float:
|
|
||||||
return self._rotate_speed
|
|
||||||
|
|
||||||
@property
|
|
||||||
def pump_state(self) -> str:
|
|
||||||
return self._pump_state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def pump_time(self) -> float:
|
|
||||||
return self._pump_time
|
|
||||||
|
|
||||||
@property
|
|
||||||
def vacuum_level(self) -> float:
|
|
||||||
return self._vacuum_level
|
|
||||||
|
|
||||||
@property
|
|
||||||
def temperature(self) -> float:
|
|
||||||
return self._temperature
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_temperature(self) -> float:
|
|
||||||
return self._target_temperature
|
|
||||||
|
|
||||||
# ==================== 设备控制方法 ====================
|
|
||||||
# 这些方法需要在注册表中添加,会作为ActionServer接受控制指令
|
|
||||||
|
|
||||||
def set_timer(self, command: str) -> str:
|
|
||||||
"""
|
|
||||||
设置定时器 - 兼容现有RotavapOne接口
|
|
||||||
|
|
||||||
Args:
|
|
||||||
command (str): JSON格式的命令字符串,包含rotate_time和pump_time
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
timer = json.loads(command)
|
|
||||||
rotate_time = timer.get("rotate_time", 0)
|
|
||||||
pump_time = timer.get("pump_time", 0)
|
|
||||||
|
|
||||||
self.success = "False"
|
|
||||||
self._rotate_time = float(rotate_time)
|
|
||||||
self._pump_time = float(pump_time)
|
|
||||||
self.success = "True"
|
|
||||||
|
|
||||||
self._status = "Timer Set"
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
except (json.JSONDecodeError, ValueError, KeyError) as e:
|
|
||||||
self._status = f"Error: Invalid command format - {str(e)}"
|
|
||||||
self.success = "False"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
def set_rotate_time(self, time_seconds: float) -> str:
|
|
||||||
"""
|
|
||||||
设置旋转时间
|
|
||||||
|
|
||||||
Args:
|
|
||||||
time_seconds (float): 旋转时间 (秒)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.success = "False"
|
|
||||||
self._rotate_time = max(0.0, float(time_seconds))
|
|
||||||
self.success = "True"
|
|
||||||
self._status = "Rotate time set"
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def set_pump_time(self, time_seconds: float) -> str:
|
|
||||||
"""
|
|
||||||
设置泵时间
|
|
||||||
|
|
||||||
Args:
|
|
||||||
time_seconds (float): 泵时间 (秒)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.success = "False"
|
|
||||||
self._pump_time = max(0.0, float(time_seconds))
|
|
||||||
self.success = "True"
|
|
||||||
self._status = "Pump time set"
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def set_rotate_speed(self, speed: float) -> str:
|
|
||||||
"""
|
|
||||||
设置旋转速度
|
|
||||||
|
|
||||||
Args:
|
|
||||||
speed (float): 旋转速度 (rpm)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
|
|
||||||
if speed < 0 or speed > self._max_rotate_speed:
|
|
||||||
self._status = f"Error: Speed out of range (0-{self._max_rotate_speed})"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._rotate_speed = speed
|
|
||||||
self._status = "Rotate speed set"
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def set_temperature(self, temperature: float) -> str:
|
|
||||||
"""
|
|
||||||
设置水浴温度
|
|
||||||
|
|
||||||
Args:
|
|
||||||
temperature (float): 目标温度 (°C)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
|
|
||||||
if temperature < 0 or temperature > self._max_temperature:
|
|
||||||
self._status = f"Error: Temperature out of range (0-{self._max_temperature})"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._target_temperature = temperature
|
|
||||||
self._status = "Temperature set"
|
|
||||||
|
|
||||||
# 启动操作线程以开始温度控制
|
|
||||||
self._start_operation()
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def start_rotation(self) -> str:
|
|
||||||
"""
|
|
||||||
启动旋转
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self._rotate_time <= 0:
|
|
||||||
self._status = "Error: No rotate time set"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._rotate_state = "Running"
|
|
||||||
self._status = "Rotation started"
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def start_pump(self) -> str:
|
|
||||||
"""
|
|
||||||
启动真空泵
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self._pump_time <= 0:
|
|
||||||
self._status = "Error: No pump time set"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._pump_state = "Running"
|
|
||||||
self._status = "Pump started"
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def stop_all_operations(self) -> str:
|
|
||||||
"""
|
|
||||||
停止所有操作
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
self._rotate_state = "Stopped"
|
|
||||||
self._pump_state = "Stopped"
|
|
||||||
self._stop_operation()
|
|
||||||
self._rotate_time = 0.0
|
|
||||||
self._pump_time = 0.0
|
|
||||||
self._vacuum_level = 0.0
|
|
||||||
self._status = "All operations stopped"
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def emergency_stop(self) -> str:
|
|
||||||
"""
|
|
||||||
紧急停止
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
self._status = "Emergency Stop"
|
|
||||||
self.stop_all_operations()
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
# ==================== 内部控制方法 ====================
|
|
||||||
|
|
||||||
def _start_operation(self):
|
|
||||||
"""
|
|
||||||
启动操作线程
|
|
||||||
|
|
||||||
这个方法启动一个后台线程来模拟旋蒸的实际运行过程。
|
|
||||||
"""
|
|
||||||
with self._thread_lock:
|
|
||||||
if not self._running:
|
|
||||||
self._running = True
|
|
||||||
self._operation_thread = threading.Thread(target=self._operation_loop)
|
|
||||||
self._operation_thread.daemon = True
|
|
||||||
self._operation_thread.start()
|
|
||||||
|
|
||||||
def _stop_operation(self):
|
|
||||||
"""
|
|
||||||
停止操作线程
|
|
||||||
|
|
||||||
安全地停止后台运行线程并等待其完成。
|
|
||||||
"""
|
|
||||||
with self._thread_lock:
|
|
||||||
self._running = False
|
|
||||||
if self._operation_thread and self._operation_thread.is_alive():
|
|
||||||
self._operation_thread.join(timeout=2.0)
|
|
||||||
|
|
||||||
def _operation_loop(self):
|
|
||||||
"""
|
|
||||||
操作主循环
|
|
||||||
|
|
||||||
这个方法在后台线程中运行,模拟真实旋蒸的工作过程:
|
|
||||||
1. 时间倒计时
|
|
||||||
2. 温度控制
|
|
||||||
3. 真空度控制
|
|
||||||
4. 状态更新
|
|
||||||
"""
|
|
||||||
while self._running:
|
|
||||||
try:
|
|
||||||
# 处理旋转时间倒计时
|
|
||||||
if self._rotate_time > 0:
|
|
||||||
self._rotate_state = "Running"
|
|
||||||
self._rotate_time = max(0.0, self._rotate_time - 1.0)
|
|
||||||
else:
|
|
||||||
self._rotate_state = "Stopped"
|
|
||||||
|
|
||||||
# 处理泵时间倒计时
|
|
||||||
if self._pump_time > 0:
|
|
||||||
self._pump_state = "Running"
|
|
||||||
self._pump_time = max(0.0, self._pump_time - 1.0)
|
|
||||||
# 模拟真空度变化
|
|
||||||
if self._vacuum_level > self._target_vacuum:
|
|
||||||
self._vacuum_level = max(self._target_vacuum, self._vacuum_level - 5.0)
|
|
||||||
else:
|
|
||||||
self._pump_state = "Stopped"
|
|
||||||
# 真空度逐渐回升
|
|
||||||
self._vacuum_level = min(1013.25, self._vacuum_level + 2.0)
|
|
||||||
|
|
||||||
# 模拟温度控制
|
|
||||||
temp_diff = self._target_temperature - self._temperature
|
|
||||||
if abs(temp_diff) > 0.5:
|
|
||||||
if temp_diff > 0:
|
|
||||||
self._temperature += min(1.0, temp_diff * 0.1)
|
|
||||||
else:
|
|
||||||
self._temperature += max(-1.0, temp_diff * 0.1)
|
|
||||||
|
|
||||||
# 更新整体状态
|
|
||||||
if self._rotate_state == "Running" or self._pump_state == "Running":
|
|
||||||
self._status = "Operating"
|
|
||||||
elif self._rotate_time > 0 or self._pump_time > 0:
|
|
||||||
self._status = "Ready"
|
|
||||||
else:
|
|
||||||
self._status = "Idle"
|
|
||||||
|
|
||||||
# 等待1秒后继续下一次循环
|
|
||||||
time.sleep(1.0)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self._status = f"Error in operation: {str(e)}"
|
|
||||||
break
|
|
||||||
|
|
||||||
# 循环结束时的清理工作
|
|
||||||
self._status = "Idle"
|
|
||||||
|
|
||||||
def get_status_info(self) -> dict:
|
|
||||||
"""
|
|
||||||
获取完整的设备状态信息
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: 包含所有设备状态的字典
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
"status": self._status,
|
|
||||||
"rotate_state": self._rotate_state,
|
|
||||||
"rotate_time": self._rotate_time,
|
|
||||||
"rotate_speed": self._rotate_speed,
|
|
||||||
"pump_state": self._pump_state,
|
|
||||||
"pump_time": self._pump_time,
|
|
||||||
"vacuum_level": self._vacuum_level,
|
|
||||||
"temperature": self._temperature,
|
|
||||||
"target_temperature": self._target_temperature,
|
|
||||||
"success": self.success,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# 用于测试的主函数
|
|
||||||
if __name__ == "__main__":
|
|
||||||
rotavap = MockRotavap()
|
|
||||||
|
|
||||||
# 测试基本功能
|
|
||||||
print("启动旋转蒸发器测试...")
|
|
||||||
print(f"初始状态: {rotavap.get_status_info()}")
|
|
||||||
|
|
||||||
# 设置定时器
|
|
||||||
timer_command = '{"rotate_time": 300, "pump_time": 600}'
|
|
||||||
rotavap.set_timer(timer_command)
|
|
||||||
|
|
||||||
# 设置温度和转速
|
|
||||||
rotavap.set_temperature(60.0)
|
|
||||||
rotavap.set_rotate_speed(120.0)
|
|
||||||
|
|
||||||
# 启动操作
|
|
||||||
rotavap.start_rotation()
|
|
||||||
rotavap.start_pump()
|
|
||||||
|
|
||||||
# 模拟运行10秒
|
|
||||||
for i in range(10):
|
|
||||||
time.sleep(1)
|
|
||||||
print(
|
|
||||||
f"第{i+1}秒: 旋转={rotavap.rotate_time:.0f}s, 泵={rotavap.pump_time:.0f}s, "
|
|
||||||
f"温度={rotavap.temperature:.1f}°C, 真空={rotavap.vacuum_level:.1f}mbar"
|
|
||||||
)
|
|
||||||
|
|
||||||
rotavap.emergency_stop()
|
|
||||||
print("测试完成")
|
|
||||||
@@ -1,399 +0,0 @@
|
|||||||
import time
|
|
||||||
import threading
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
|
|
||||||
class MockSeparator:
|
|
||||||
def __init__(self, port: str = "MOCK"):
|
|
||||||
self.port = port
|
|
||||||
|
|
||||||
# 基本状态属性
|
|
||||||
self._status: str = "Idle" # 当前总体状态
|
|
||||||
self._valve_state: str = "Closed" # 阀门状态:Open 或 Closed
|
|
||||||
self._settling_time: float = 0.0 # 静置时间(秒)
|
|
||||||
|
|
||||||
# 搅拌相关属性
|
|
||||||
self._shake_time: float = 0.0 # 剩余摇摆时间(秒)
|
|
||||||
self._shake_status: str = "Not Shaking" # 摇摆状态
|
|
||||||
|
|
||||||
# 用于后台模拟 shake 动作
|
|
||||||
self._operation_thread = None
|
|
||||||
self._thread_lock = threading.Lock()
|
|
||||||
self._running = False
|
|
||||||
|
|
||||||
# Separate action 相关属性
|
|
||||||
self._current_device: str = "MockSeparator1"
|
|
||||||
self._purpose: str = "" # wash or extract
|
|
||||||
self._product_phase: str = "" # top or bottom
|
|
||||||
self._from_vessel: str = ""
|
|
||||||
self._separation_vessel: str = ""
|
|
||||||
self._to_vessel: str = ""
|
|
||||||
self._waste_phase_to_vessel: str = ""
|
|
||||||
self._solvent: str = ""
|
|
||||||
self._solvent_volume: float = 0.0
|
|
||||||
self._through: str = ""
|
|
||||||
self._repeats: int = 1
|
|
||||||
self._stir_time: float = 0.0
|
|
||||||
self._stir_speed: float = 0.0
|
|
||||||
self._time_spent = timedelta()
|
|
||||||
self._time_remaining = timedelta()
|
|
||||||
self._start_time = datetime.now() # 添加这一行
|
|
||||||
|
|
||||||
@property
|
|
||||||
def current_device(self) -> str:
|
|
||||||
return self._current_device
|
|
||||||
|
|
||||||
@property
|
|
||||||
def purpose(self) -> str:
|
|
||||||
return self._purpose
|
|
||||||
|
|
||||||
@property
|
|
||||||
def valve_state(self) -> str:
|
|
||||||
return self._valve_state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def settling_time(self) -> float:
|
|
||||||
return self._settling_time
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status(self) -> str:
|
|
||||||
return self._status
|
|
||||||
|
|
||||||
@property
|
|
||||||
def shake_time(self) -> float:
|
|
||||||
with self._thread_lock:
|
|
||||||
return self._shake_time
|
|
||||||
|
|
||||||
@property
|
|
||||||
def shake_status(self) -> str:
|
|
||||||
with self._thread_lock:
|
|
||||||
return self._shake_status
|
|
||||||
|
|
||||||
@property
|
|
||||||
def product_phase(self) -> str:
|
|
||||||
return self._product_phase
|
|
||||||
|
|
||||||
@property
|
|
||||||
def from_vessel(self) -> str:
|
|
||||||
return self._from_vessel
|
|
||||||
|
|
||||||
@property
|
|
||||||
def separation_vessel(self) -> str:
|
|
||||||
return self._separation_vessel
|
|
||||||
|
|
||||||
@property
|
|
||||||
def to_vessel(self) -> str:
|
|
||||||
return self._to_vessel
|
|
||||||
|
|
||||||
@property
|
|
||||||
def waste_phase_to_vessel(self) -> str:
|
|
||||||
return self._waste_phase_to_vessel
|
|
||||||
|
|
||||||
@property
|
|
||||||
def solvent(self) -> str:
|
|
||||||
return self._solvent
|
|
||||||
|
|
||||||
@property
|
|
||||||
def solvent_volume(self) -> float:
|
|
||||||
return self._solvent_volume
|
|
||||||
|
|
||||||
@property
|
|
||||||
def through(self) -> str:
|
|
||||||
return self._through
|
|
||||||
|
|
||||||
@property
|
|
||||||
def repeats(self) -> int:
|
|
||||||
return self._repeats
|
|
||||||
|
|
||||||
@property
|
|
||||||
def stir_time(self) -> float:
|
|
||||||
return self._stir_time
|
|
||||||
|
|
||||||
@property
|
|
||||||
def stir_speed(self) -> float:
|
|
||||||
return self._stir_speed
|
|
||||||
|
|
||||||
@property
|
|
||||||
def time_spent(self) -> float:
|
|
||||||
if self._running:
|
|
||||||
self._time_spent = datetime.now() - self._start_time
|
|
||||||
return self._time_spent.total_seconds()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def time_remaining(self) -> float:
|
|
||||||
if self._running:
|
|
||||||
elapsed = (datetime.now() - self._start_time).total_seconds()
|
|
||||||
total_time = (self._stir_time + self._settling_time + 10) * self._repeats
|
|
||||||
remain = max(0, total_time - elapsed)
|
|
||||||
self._time_remaining = timedelta(seconds=remain)
|
|
||||||
return self._time_remaining.total_seconds()
|
|
||||||
|
|
||||||
def separate(self, purpose: str, product_phase: str, from_vessel: str,
|
|
||||||
separation_vessel: str, to_vessel: str, waste_phase_to_vessel: str = "",
|
|
||||||
solvent: str = "", solvent_volume: float = 0.0, through: str = "",
|
|
||||||
repeats: int = 1, stir_time: float = 0.0, stir_speed: float = 0.0,
|
|
||||||
settling_time: float = 60.0) -> dict:
|
|
||||||
"""
|
|
||||||
执行分离操作
|
|
||||||
"""
|
|
||||||
with self._thread_lock:
|
|
||||||
# 检查是否已经在运行
|
|
||||||
if self._running:
|
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"status": "Error: Operation already in progress"
|
|
||||||
}
|
|
||||||
# 必填参数验证
|
|
||||||
if not all([from_vessel, separation_vessel, to_vessel]):
|
|
||||||
self._status = "Error: Missing required vessel parameters"
|
|
||||||
return {"success": False}
|
|
||||||
# 验证参数
|
|
||||||
if purpose not in ["wash", "extract"]:
|
|
||||||
self._status = "Error: Invalid purpose"
|
|
||||||
return {"success": False}
|
|
||||||
|
|
||||||
if product_phase not in ["top", "bottom"]:
|
|
||||||
self._status = "Error: Invalid product phase"
|
|
||||||
return {"success": False}
|
|
||||||
# 数值参数验证
|
|
||||||
try:
|
|
||||||
solvent_volume = float(solvent_volume)
|
|
||||||
repeats = int(repeats)
|
|
||||||
stir_time = float(stir_time)
|
|
||||||
stir_speed = float(stir_speed)
|
|
||||||
settling_time = float(settling_time)
|
|
||||||
except ValueError:
|
|
||||||
self._status = "Error: Invalid numeric parameters"
|
|
||||||
return {"success": False}
|
|
||||||
|
|
||||||
# 设置参数
|
|
||||||
self._purpose = purpose
|
|
||||||
self._product_phase = product_phase
|
|
||||||
self._from_vessel = from_vessel
|
|
||||||
self._separation_vessel = separation_vessel
|
|
||||||
self._to_vessel = to_vessel
|
|
||||||
self._waste_phase_to_vessel = waste_phase_to_vessel
|
|
||||||
self._solvent = solvent
|
|
||||||
self._solvent_volume = float(solvent_volume)
|
|
||||||
self._through = through
|
|
||||||
self._repeats = int(repeats)
|
|
||||||
self._stir_time = float(stir_time)
|
|
||||||
self._stir_speed = float(stir_speed)
|
|
||||||
self._settling_time = float(settling_time)
|
|
||||||
|
|
||||||
# 重置计时器
|
|
||||||
self._start_time = datetime.now()
|
|
||||||
self._time_spent = timedelta()
|
|
||||||
total_time = (self._stir_time + self._settling_time + 10) * self._repeats
|
|
||||||
self._time_remaining = timedelta(seconds=total_time)
|
|
||||||
|
|
||||||
# 启动分离操作
|
|
||||||
self._status = "Starting Separation"
|
|
||||||
self._running = True
|
|
||||||
|
|
||||||
# 在锁内创建和启动线程
|
|
||||||
self._operation_thread = threading.Thread(target=self._operation_loop)
|
|
||||||
self._operation_thread.daemon = True
|
|
||||||
self._operation_thread.start()
|
|
||||||
|
|
||||||
# 等待确认操作已经开始
|
|
||||||
time.sleep(0.1) # 短暂等待确保操作线程已启动
|
|
||||||
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"status": self._status,
|
|
||||||
"current_device": self._current_device,
|
|
||||||
"time_spent": self._time_spent.total_seconds(),
|
|
||||||
"time_remaining": self._time_remaining.total_seconds()
|
|
||||||
}
|
|
||||||
|
|
||||||
def shake(self, shake_time: float) -> str:
|
|
||||||
"""
|
|
||||||
模拟 shake(搅拌)操作:
|
|
||||||
- 进入 "Shaking" 状态,倒计时 shake_time 秒
|
|
||||||
- shake 结束后,进入 "Settling" 状态,静置时间固定为 5 秒
|
|
||||||
- 最后恢复为 Idle
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
shake_time = float(shake_time)
|
|
||||||
except ValueError:
|
|
||||||
self._status = "Error: Invalid shake time"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
with self._thread_lock:
|
|
||||||
self._status = "Shaking"
|
|
||||||
self._settling_time = 0.0
|
|
||||||
self._shake_time = shake_time
|
|
||||||
self._shake_status = "Shaking"
|
|
||||||
|
|
||||||
def _run_shake():
|
|
||||||
remaining = shake_time
|
|
||||||
while remaining > 0:
|
|
||||||
time.sleep(1)
|
|
||||||
remaining -= 1
|
|
||||||
with self._thread_lock:
|
|
||||||
self._shake_time = remaining
|
|
||||||
with self._thread_lock:
|
|
||||||
self._status = "Settling"
|
|
||||||
self._settling_time = 60.0 # 固定静置时间为60秒
|
|
||||||
self._shake_status = "Settling"
|
|
||||||
while True:
|
|
||||||
with self._thread_lock:
|
|
||||||
if self._settling_time <= 0:
|
|
||||||
self._status = "Idle"
|
|
||||||
self._shake_status = "Idle"
|
|
||||||
break
|
|
||||||
time.sleep(1)
|
|
||||||
with self._thread_lock:
|
|
||||||
self._settling_time = max(0.0, self._settling_time - 1)
|
|
||||||
|
|
||||||
self._operation_thread = threading.Thread(target=_run_shake)
|
|
||||||
self._operation_thread.daemon = True
|
|
||||||
self._operation_thread.start()
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def set_valve(self, command: str) -> str:
|
|
||||||
"""
|
|
||||||
阀门控制命令:传入 "open" 或 "close"
|
|
||||||
"""
|
|
||||||
|
|
||||||
command = command.lower()
|
|
||||||
if command == "open":
|
|
||||||
self._valve_state = "Open"
|
|
||||||
self._status = "Valve Opened"
|
|
||||||
elif command == "close":
|
|
||||||
self._valve_state = "Closed"
|
|
||||||
self._status = "Valve Closed"
|
|
||||||
else:
|
|
||||||
self._status = "Error: Invalid valve command"
|
|
||||||
return "Error"
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def _operation_loop(self):
|
|
||||||
"""分离操作主循环"""
|
|
||||||
try:
|
|
||||||
current_repeat = 1
|
|
||||||
|
|
||||||
# 立即更新状态,确保不会停留在Starting Separation
|
|
||||||
with self._thread_lock:
|
|
||||||
self._status = f"Separation Cycle {current_repeat}/{self._repeats}"
|
|
||||||
|
|
||||||
while self._running and current_repeat <= self._repeats:
|
|
||||||
# 第一步:搅拌
|
|
||||||
if self._stir_time > 0:
|
|
||||||
with self._thread_lock:
|
|
||||||
self._status = f"Stirring (Repeat {current_repeat}/{self._repeats})"
|
|
||||||
remaining_stir = self._stir_time
|
|
||||||
while remaining_stir > 0 and self._running:
|
|
||||||
time.sleep(1)
|
|
||||||
remaining_stir -= 1
|
|
||||||
|
|
||||||
# 第二步:静置
|
|
||||||
if self._settling_time > 0:
|
|
||||||
with self._thread_lock:
|
|
||||||
self._status = f"Settling (Repeat {current_repeat}/{self._repeats})"
|
|
||||||
remaining_settle = self._settling_time
|
|
||||||
while remaining_settle > 0 and self._running:
|
|
||||||
time.sleep(1)
|
|
||||||
remaining_settle -= 1
|
|
||||||
|
|
||||||
# 第三步:打开阀门排出
|
|
||||||
with self._thread_lock:
|
|
||||||
self._valve_state = "Open"
|
|
||||||
self._status = f"Draining (Repeat {current_repeat}/{self._repeats})"
|
|
||||||
|
|
||||||
# 模拟排出时间(5秒)
|
|
||||||
time.sleep(10)
|
|
||||||
|
|
||||||
# 关闭阀门
|
|
||||||
with self._thread_lock:
|
|
||||||
self._valve_state = "Closed"
|
|
||||||
|
|
||||||
# 检查是否继续下一次重复
|
|
||||||
if current_repeat < self._repeats:
|
|
||||||
current_repeat += 1
|
|
||||||
else:
|
|
||||||
with self._thread_lock:
|
|
||||||
self._status = "Separation Complete"
|
|
||||||
break
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
with self._thread_lock:
|
|
||||||
self._status = f"Error in separation: {str(e)}"
|
|
||||||
finally:
|
|
||||||
with self._thread_lock:
|
|
||||||
self._running = False
|
|
||||||
self._valve_state = "Closed"
|
|
||||||
if self._status == "Starting Separation":
|
|
||||||
self._status = "Error: Operation failed to start"
|
|
||||||
elif self._status != "Separation Complete":
|
|
||||||
self._status = "Stopped"
|
|
||||||
|
|
||||||
def stop_operations(self) -> str:
|
|
||||||
"""停止任何正在执行的操作"""
|
|
||||||
with self._thread_lock:
|
|
||||||
self._running = False
|
|
||||||
if self._operation_thread and self._operation_thread.is_alive():
|
|
||||||
self._operation_thread.join(timeout=1.0)
|
|
||||||
self._operation_thread = None
|
|
||||||
self._settling_time = 0.0
|
|
||||||
self._status = "Idle"
|
|
||||||
self._shake_status = "Idle"
|
|
||||||
self._shake_time = 0.0
|
|
||||||
self._time_remaining = timedelta()
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def get_status_info(self) -> dict:
|
|
||||||
"""获取当前设备状态信息"""
|
|
||||||
with self._thread_lock:
|
|
||||||
current_time = datetime.now()
|
|
||||||
if self._start_time:
|
|
||||||
self._time_spent = current_time - self._start_time
|
|
||||||
|
|
||||||
return {
|
|
||||||
"status": self._status,
|
|
||||||
"valve_state": self._valve_state,
|
|
||||||
"settling_time": self._settling_time,
|
|
||||||
"shake_time": self._shake_time,
|
|
||||||
"shake_status": self._shake_status,
|
|
||||||
"current_device": self._current_device,
|
|
||||||
"purpose": self._purpose,
|
|
||||||
"product_phase": self._product_phase,
|
|
||||||
"from_vessel": self._from_vessel,
|
|
||||||
"separation_vessel": self._separation_vessel,
|
|
||||||
"to_vessel": self._to_vessel,
|
|
||||||
"waste_phase_to_vessel": self._waste_phase_to_vessel,
|
|
||||||
"solvent": self._solvent,
|
|
||||||
"solvent_volume": self._solvent_volume,
|
|
||||||
"through": self._through,
|
|
||||||
"repeats": self._repeats,
|
|
||||||
"stir_time": self._stir_time,
|
|
||||||
"stir_speed": self._stir_speed,
|
|
||||||
"time_spent": self._time_spent.total_seconds(),
|
|
||||||
"time_remaining": self._time_remaining.total_seconds()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# 主函数用于测试
|
|
||||||
if __name__ == "__main__":
|
|
||||||
separator = MockSeparator()
|
|
||||||
|
|
||||||
print("启动简单版分离器测试...")
|
|
||||||
print("初始状态:", separator.get_status_info())
|
|
||||||
|
|
||||||
# 触发 shake 操作,模拟 10 秒的搅拌
|
|
||||||
print("执行 shake 操作...")
|
|
||||||
print(separator.shake(10.0))
|
|
||||||
|
|
||||||
# 循环显示状态变化
|
|
||||||
for i in range(20):
|
|
||||||
time.sleep(1)
|
|
||||||
info = separator.get_status_info()
|
|
||||||
print(
|
|
||||||
f"第{i+1}秒: 状态={info['status']}, 静置时间={info['settling_time']:.1f}秒, "
|
|
||||||
f"阀门状态={info['valve_state']}, shake_time={info['shake_time']:.1f}, "
|
|
||||||
f"shake_status={info['shake_status']}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 模拟打开阀门
|
|
||||||
print("打开阀门...", separator.set_valve("open"))
|
|
||||||
print("最终状态:", separator.get_status_info())
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
import time
|
|
||||||
|
|
||||||
|
|
||||||
class MockSolenoidValve:
|
|
||||||
"""
|
|
||||||
模拟电磁阀设备类 - 简化版本
|
|
||||||
|
|
||||||
这个类提供了电磁阀的基本功能:开启、关闭和状态查询
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, port: str = "MOCK"):
|
|
||||||
"""
|
|
||||||
初始化MockSolenoidValve实例
|
|
||||||
|
|
||||||
Args:
|
|
||||||
port (str): 设备端口,默认为"MOCK"表示模拟设备
|
|
||||||
"""
|
|
||||||
self.port = port
|
|
||||||
self._status: str = "Idle"
|
|
||||||
self._valve_status: str = "Closed" # 阀门位置:Open, Closed
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status(self) -> str:
|
|
||||||
"""设备状态 - 会被自动识别的设备属性"""
|
|
||||||
return self._status
|
|
||||||
|
|
||||||
@property
|
|
||||||
def valve_status(self) -> str:
|
|
||||||
"""阀门状态"""
|
|
||||||
return self._valve_status
|
|
||||||
|
|
||||||
def set_valve_status(self, status: str) -> str:
|
|
||||||
"""
|
|
||||||
设置阀门位置
|
|
||||||
|
|
||||||
Args:
|
|
||||||
position (str): 阀门位置,可选值:"Open", "Closed"
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
if status not in ["Open", "Closed"]:
|
|
||||||
self._status = "Error: Invalid position"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._status = "Moving"
|
|
||||||
time.sleep(1) # 模拟阀门动作时间
|
|
||||||
|
|
||||||
self._valve_status = status
|
|
||||||
self._status = "Idle"
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def open_valve(self) -> str:
|
|
||||||
"""打开阀门"""
|
|
||||||
return self.set_valve_status("Open")
|
|
||||||
|
|
||||||
def close_valve(self) -> str:
|
|
||||||
"""关闭阀门"""
|
|
||||||
return self.set_valve_status("Closed")
|
|
||||||
|
|
||||||
def get_valve_status(self) -> str:
|
|
||||||
"""获取阀门位置"""
|
|
||||||
return self._valve_status
|
|
||||||
|
|
||||||
def is_open(self) -> bool:
|
|
||||||
"""检查阀门是否打开"""
|
|
||||||
return self._valve_status == "Open"
|
|
||||||
|
|
||||||
def is_closed(self) -> bool:
|
|
||||||
"""检查阀门是否关闭"""
|
|
||||||
return self._valve_status == "Closed"
|
|
||||||
|
|
||||||
|
|
||||||
# 用于测试的主函数
|
|
||||||
if __name__ == "__main__":
|
|
||||||
valve = MockSolenoidValve()
|
|
||||||
|
|
||||||
print("启动电磁阀测试...")
|
|
||||||
print(f"初始状态: 位置={valve.valve_status}, 状态={valve.status}")
|
|
||||||
|
|
||||||
# 测试开启阀门
|
|
||||||
valve.open_valve()
|
|
||||||
print(f"开启后: 位置={valve.valve_status}, 状态={valve.status}")
|
|
||||||
|
|
||||||
# 测试关闭阀门
|
|
||||||
valve.close_valve()
|
|
||||||
print(f"关闭后: 位置={valve.valve_status}, 状态={valve.status}")
|
|
||||||
|
|
||||||
print("测试完成")
|
|
||||||
@@ -1,307 +0,0 @@
|
|||||||
import time
|
|
||||||
import threading
|
|
||||||
|
|
||||||
|
|
||||||
class MockStirrer:
|
|
||||||
def __init__(self, port: str = "MOCK"):
|
|
||||||
self.port = port
|
|
||||||
|
|
||||||
# 设备基本状态属性
|
|
||||||
self._status: str = "Idle" # 设备状态:Idle, Running, Error, Stopped
|
|
||||||
|
|
||||||
# 搅拌相关属性
|
|
||||||
self._stir_speed: float = 0.0 # 当前搅拌速度 (rpm)
|
|
||||||
self._target_stir_speed: float = 0.0 # 目标搅拌速度 (rpm)
|
|
||||||
self._max_stir_speed: float = 2000.0 # 最大搅拌速度 (rpm)
|
|
||||||
self._stir_state: str = "Stopped" # 搅拌状态:Running, Stopped
|
|
||||||
|
|
||||||
# 温度相关属性
|
|
||||||
self._temperature: float = 25.0 # 当前温度 (°C)
|
|
||||||
self._target_temperature: float = 25.0 # 目标温度 (°C)
|
|
||||||
self._max_temperature: float = 300.0 # 最大温度 (°C)
|
|
||||||
self._heating_state: str = "Off" # 加热状态:On, Off
|
|
||||||
self._heating_power: float = 0.0 # 加热功率百分比 0-100
|
|
||||||
|
|
||||||
# 运行控制线程
|
|
||||||
self._operation_thread = None
|
|
||||||
self._running = False
|
|
||||||
self._thread_lock = threading.Lock()
|
|
||||||
|
|
||||||
# ==================== 状态属性 ====================
|
|
||||||
# 这些属性会被Uni-Lab系统自动识别并定时对外广播
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status(self) -> str:
|
|
||||||
return self._status
|
|
||||||
|
|
||||||
@property
|
|
||||||
def stir_speed(self) -> float:
|
|
||||||
return self._stir_speed
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_stir_speed(self) -> float:
|
|
||||||
return self._target_stir_speed
|
|
||||||
|
|
||||||
@property
|
|
||||||
def stir_state(self) -> str:
|
|
||||||
return self._stir_state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def temperature(self) -> float:
|
|
||||||
"""
|
|
||||||
当前温度
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 当前温度 (°C)
|
|
||||||
"""
|
|
||||||
return self._temperature
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_temperature(self) -> float:
|
|
||||||
"""
|
|
||||||
目标温度
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 目标温度 (°C)
|
|
||||||
"""
|
|
||||||
return self._target_temperature
|
|
||||||
|
|
||||||
@property
|
|
||||||
def heating_state(self) -> str:
|
|
||||||
return self._heating_state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def heating_power(self) -> float:
|
|
||||||
return self._heating_power
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_stir_speed(self) -> float:
|
|
||||||
return self._max_stir_speed
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_temperature(self) -> float:
|
|
||||||
return self._max_temperature
|
|
||||||
|
|
||||||
# ==================== 设备控制方法 ====================
|
|
||||||
# 这些方法需要在注册表中添加,会作为ActionServer接受控制指令
|
|
||||||
|
|
||||||
def set_stir_speed(self, speed: float) -> str:
|
|
||||||
|
|
||||||
speed = float(speed) # 确保传入的速度是浮点数
|
|
||||||
|
|
||||||
if speed < 0 or speed > self._max_stir_speed:
|
|
||||||
self._status = f"Error: Speed out of range (0-{self._max_stir_speed})"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._target_stir_speed = speed
|
|
||||||
self._status = "Setting Stir Speed"
|
|
||||||
|
|
||||||
# 如果设置了非零速度,启动搅拌
|
|
||||||
if speed > 0:
|
|
||||||
self._stir_state = "Running"
|
|
||||||
else:
|
|
||||||
self._stir_state = "Stopped"
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def set_temperature(self, temperature: float) -> str:
|
|
||||||
temperature = float(temperature) # 确保传入的温度是浮点数
|
|
||||||
|
|
||||||
if temperature < 0 or temperature > self._max_temperature:
|
|
||||||
self._status = f"Error: Temperature out of range (0-{self._max_temperature})"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._target_temperature = temperature
|
|
||||||
self._status = "Setting Temperature"
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def start_stirring(self) -> str:
|
|
||||||
|
|
||||||
if self._target_stir_speed <= 0:
|
|
||||||
self._status = "Error: No target speed set"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._stir_state = "Running"
|
|
||||||
self._status = "Stirring Started"
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def stop_stirring(self) -> str:
|
|
||||||
self._stir_state = "Stopped"
|
|
||||||
self._target_stir_speed = 0.0
|
|
||||||
self._status = "Stirring Stopped"
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def heating_control(self, heating_state: str = "On") -> str:
|
|
||||||
|
|
||||||
if heating_state not in ["On", "Off"]:
|
|
||||||
self._status = "Error: Invalid heating state"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._heating_state = heating_state
|
|
||||||
|
|
||||||
if heating_state == "On":
|
|
||||||
self._status = "Heating On"
|
|
||||||
else:
|
|
||||||
self._status = "Heating Off"
|
|
||||||
self._heating_power = 0.0
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def stop_all_operations(self) -> str:
|
|
||||||
self._stir_state = "Stopped"
|
|
||||||
self._heating_state = "Off"
|
|
||||||
self._stop_operation()
|
|
||||||
self._stir_speed = 0.0
|
|
||||||
self._target_stir_speed = 0.0
|
|
||||||
self._heating_power = 0.0
|
|
||||||
self._status = "All operations stopped"
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def emergency_stop(self) -> str:
|
|
||||||
"""
|
|
||||||
紧急停止
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
self._status = "Emergency Stop"
|
|
||||||
self.stop_all_operations()
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
# ==================== 内部控制方法 ====================
|
|
||||||
|
|
||||||
def _start_operation(self):
|
|
||||||
with self._thread_lock:
|
|
||||||
if not self._running:
|
|
||||||
self._running = True
|
|
||||||
self._operation_thread = threading.Thread(target=self._operation_loop)
|
|
||||||
self._operation_thread.daemon = True
|
|
||||||
self._operation_thread.start()
|
|
||||||
|
|
||||||
def _stop_operation(self):
|
|
||||||
"""
|
|
||||||
停止操作线程
|
|
||||||
|
|
||||||
安全地停止后台运行线程并等待其完成。
|
|
||||||
"""
|
|
||||||
with self._thread_lock:
|
|
||||||
self._running = False
|
|
||||||
if self._operation_thread and self._operation_thread.is_alive():
|
|
||||||
self._operation_thread.join(timeout=2.0)
|
|
||||||
|
|
||||||
def _operation_loop(self):
|
|
||||||
while self._running:
|
|
||||||
try:
|
|
||||||
# 处理搅拌速度控制
|
|
||||||
if self._stir_state == "Running":
|
|
||||||
speed_diff = self._target_stir_speed - self._stir_speed
|
|
||||||
|
|
||||||
if abs(speed_diff) < 1.0: # 速度接近目标值
|
|
||||||
self._stir_speed = self._target_stir_speed
|
|
||||||
if self._stir_speed > 0:
|
|
||||||
self._status = "Stirring at Target Speed"
|
|
||||||
else:
|
|
||||||
# 模拟速度调节,每秒调整10%的差值
|
|
||||||
adjustment = speed_diff * 0.1
|
|
||||||
self._stir_speed += adjustment
|
|
||||||
self._status = "Adjusting Stir Speed"
|
|
||||||
|
|
||||||
# 确保速度在合理范围内
|
|
||||||
self._stir_speed = max(0.0, min(self._max_stir_speed, self._stir_speed))
|
|
||||||
else:
|
|
||||||
# 搅拌停止时,速度逐渐降为0
|
|
||||||
if self._stir_speed > 0:
|
|
||||||
self._stir_speed = max(0.0, self._stir_speed - 50.0) # 每秒减少50rpm
|
|
||||||
|
|
||||||
# 处理温度控制
|
|
||||||
if self._heating_state == "On":
|
|
||||||
temp_diff = self._target_temperature - self._temperature
|
|
||||||
|
|
||||||
if abs(temp_diff) < 0.5: # 温度接近目标值
|
|
||||||
self._heating_power = 20.0 # 维持温度的最小功率
|
|
||||||
elif temp_diff > 0: # 需要加热
|
|
||||||
# 根据温差调整加热功率
|
|
||||||
if temp_diff > 50:
|
|
||||||
self._heating_power = 100.0
|
|
||||||
elif temp_diff > 20:
|
|
||||||
self._heating_power = 80.0
|
|
||||||
elif temp_diff > 10:
|
|
||||||
self._heating_power = 60.0
|
|
||||||
else:
|
|
||||||
self._heating_power = 40.0
|
|
||||||
|
|
||||||
# 模拟加热过程
|
|
||||||
heating_rate = self._heating_power / 100.0 * 1.5 # 最大每秒升温1.5度
|
|
||||||
self._temperature += heating_rate
|
|
||||||
else: # 目标温度低于当前温度
|
|
||||||
self._heating_power = 0.0
|
|
||||||
# 自然冷却
|
|
||||||
self._temperature -= 0.1
|
|
||||||
else:
|
|
||||||
self._heating_power = 0.0
|
|
||||||
# 自然冷却到室温
|
|
||||||
if self._temperature > 25.0:
|
|
||||||
self._temperature -= 0.2
|
|
||||||
|
|
||||||
# 限制温度范围
|
|
||||||
self._temperature = max(20.0, min(self._max_temperature, self._temperature))
|
|
||||||
|
|
||||||
# 更新整体状态
|
|
||||||
if self._stir_state == "Running" and self._heating_state == "On":
|
|
||||||
self._status = "Stirring and Heating"
|
|
||||||
elif self._stir_state == "Running":
|
|
||||||
self._status = "Stirring Only"
|
|
||||||
elif self._heating_state == "On":
|
|
||||||
self._status = "Heating Only"
|
|
||||||
else:
|
|
||||||
self._status = "Idle"
|
|
||||||
|
|
||||||
# 等待1秒后继续下一次循环
|
|
||||||
time.sleep(1.0)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self._status = f"Error in operation: {str(e)}"
|
|
||||||
break
|
|
||||||
|
|
||||||
# 循环结束时的清理工作
|
|
||||||
self._status = "Idle"
|
|
||||||
|
|
||||||
def get_status_info(self) -> dict:
|
|
||||||
return {
|
|
||||||
"status": self._status,
|
|
||||||
"stir_speed": self._stir_speed,
|
|
||||||
"target_stir_speed": self._target_stir_speed,
|
|
||||||
"stir_state": self._stir_state,
|
|
||||||
"temperature": self._temperature,
|
|
||||||
"target_temperature": self._target_temperature,
|
|
||||||
"heating_state": self._heating_state,
|
|
||||||
"heating_power": self._heating_power,
|
|
||||||
"max_stir_speed": self._max_stir_speed,
|
|
||||||
"max_temperature": self._max_temperature,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# 用于测试的主函数
|
|
||||||
if __name__ == "__main__":
|
|
||||||
stirrer = MockStirrer()
|
|
||||||
|
|
||||||
# 测试基本功能
|
|
||||||
print("启动搅拌器测试...")
|
|
||||||
print(f"初始状态: {stirrer.get_status_info()}")
|
|
||||||
|
|
||||||
# 设置搅拌速度和温度
|
|
||||||
stirrer.set_stir_speed(800.0)
|
|
||||||
stirrer.set_temperature(60.0)
|
|
||||||
stirrer.heating_control("On")
|
|
||||||
|
|
||||||
# 模拟运行15秒
|
|
||||||
for i in range(15):
|
|
||||||
time.sleep(1)
|
|
||||||
print(
|
|
||||||
f"第{i+1}秒: 速度={stirrer.stir_speed:.0f}rpm, 温度={stirrer.temperature:.1f}°C, "
|
|
||||||
f"功率={stirrer.heating_power:.1f}%, 状态={stirrer.status}"
|
|
||||||
)
|
|
||||||
|
|
||||||
stirrer.emergency_stop()
|
|
||||||
print("测试完成")
|
|
||||||
@@ -1,229 +0,0 @@
|
|||||||
import time
|
|
||||||
import threading
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
|
|
||||||
class MockStirrer_new:
|
|
||||||
def __init__(self, port: str = "MOCK"):
|
|
||||||
self.port = port
|
|
||||||
|
|
||||||
# 基本状态属性
|
|
||||||
self._status: str = "Idle"
|
|
||||||
self._vessel: str = ""
|
|
||||||
self._purpose: str = ""
|
|
||||||
|
|
||||||
# 搅拌相关属性
|
|
||||||
self._stir_speed: float = 0.0
|
|
||||||
self._target_stir_speed: float = 0.0
|
|
||||||
self._max_stir_speed: float = 2000.0
|
|
||||||
self._stir_state: str = "Stopped"
|
|
||||||
|
|
||||||
# 计时相关
|
|
||||||
self._stir_time: float = 0.0
|
|
||||||
self._settling_time: float = 0.0
|
|
||||||
self._start_time = datetime.now()
|
|
||||||
self._time_remaining = timedelta()
|
|
||||||
|
|
||||||
# 运行控制
|
|
||||||
self._operation_thread = None
|
|
||||||
self._running = False
|
|
||||||
self._thread_lock = threading.Lock()
|
|
||||||
|
|
||||||
# 创建操作线程
|
|
||||||
self._operation_thread = threading.Thread(target=self._operation_loop)
|
|
||||||
self._operation_thread.daemon = True
|
|
||||||
self._operation_thread.start()
|
|
||||||
|
|
||||||
# ==================== 状态属性 ====================
|
|
||||||
@property
|
|
||||||
def status(self) -> str:
|
|
||||||
return self._status
|
|
||||||
|
|
||||||
@property
|
|
||||||
def stir_speed(self) -> float:
|
|
||||||
return self._stir_speed
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_stir_speed(self) -> float:
|
|
||||||
return self._target_stir_speed
|
|
||||||
|
|
||||||
@property
|
|
||||||
def stir_state(self) -> str:
|
|
||||||
return self._stir_state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def vessel(self) -> str:
|
|
||||||
return self._vessel
|
|
||||||
|
|
||||||
@property
|
|
||||||
def purpose(self) -> str:
|
|
||||||
return self._purpose
|
|
||||||
|
|
||||||
@property
|
|
||||||
def stir_time(self) -> float:
|
|
||||||
return self._stir_time
|
|
||||||
|
|
||||||
@property
|
|
||||||
def settling_time(self) -> float:
|
|
||||||
return self._settling_time
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_stir_speed(self) -> float:
|
|
||||||
return self._max_stir_speed
|
|
||||||
|
|
||||||
@property
|
|
||||||
def progress(self) -> float:
|
|
||||||
"""返回当前操作的进度(0-100)"""
|
|
||||||
if not self._running:
|
|
||||||
return 0.0
|
|
||||||
elapsed = (datetime.now() - self._start_time).total_seconds()
|
|
||||||
total_time = self._stir_time + self._settling_time
|
|
||||||
if total_time <= 0:
|
|
||||||
return 100.0
|
|
||||||
return min(100.0, (elapsed / total_time) * 100)
|
|
||||||
|
|
||||||
# ==================== Action Server 方法 ====================
|
|
||||||
def start_stir(self, vessel: str, stir_speed: float = 0.0, purpose: str = "") -> dict:
|
|
||||||
"""
|
|
||||||
StartStir.action 对应的方法
|
|
||||||
"""
|
|
||||||
with self._thread_lock:
|
|
||||||
if self._running:
|
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"message": "Operation already in progress"
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 重置所有参数
|
|
||||||
self._vessel = vessel
|
|
||||||
self._purpose = purpose
|
|
||||||
self._stir_time = 0.0 # 连续搅拌模式下不设置搅拌时间
|
|
||||||
self._settling_time = 0.0
|
|
||||||
self._start_time = datetime.now() # 重置开始时间
|
|
||||||
|
|
||||||
if stir_speed > 0:
|
|
||||||
self._target_stir_speed = min(stir_speed, self._max_stir_speed)
|
|
||||||
|
|
||||||
self._stir_state = "Running"
|
|
||||||
self._status = "Stirring Started"
|
|
||||||
self._running = True
|
|
||||||
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"message": "Stirring started successfully"
|
|
||||||
}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"message": f"Error: {str(e)}"
|
|
||||||
}
|
|
||||||
|
|
||||||
def stir(self, stir_time: float, stir_speed: float, settling_time: float) -> dict:
|
|
||||||
"""
|
|
||||||
Stir.action 对应的方法
|
|
||||||
"""
|
|
||||||
with self._thread_lock:
|
|
||||||
try:
|
|
||||||
# 如果已经在运行,先停止当前操作
|
|
||||||
if self._running:
|
|
||||||
self._running = False
|
|
||||||
self._stir_state = "Stopped"
|
|
||||||
self._target_stir_speed = 0.0
|
|
||||||
time.sleep(0.1) # 给一个短暂的停止时间
|
|
||||||
|
|
||||||
|
|
||||||
# 重置所有参数
|
|
||||||
self._stir_time = float(stir_time)
|
|
||||||
self._settling_time = float(settling_time)
|
|
||||||
self._target_stir_speed = min(float(stir_speed), self._max_stir_speed)
|
|
||||||
self._start_time = datetime.now() # 重置开始时间
|
|
||||||
self._stir_state = "Running"
|
|
||||||
self._status = "Stirring"
|
|
||||||
self._running = True
|
|
||||||
|
|
||||||
return {"success": True}
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
self._status = "Error: Invalid parameters"
|
|
||||||
return {"success": False}
|
|
||||||
|
|
||||||
def stop_stir(self, vessel: str) -> dict:
|
|
||||||
"""
|
|
||||||
StopStir.action 对应的方法
|
|
||||||
"""
|
|
||||||
with self._thread_lock:
|
|
||||||
if vessel != self._vessel:
|
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"message": "Vessel mismatch"
|
|
||||||
}
|
|
||||||
|
|
||||||
self._running = False
|
|
||||||
self._stir_state = "Stopped"
|
|
||||||
self._target_stir_speed = 0.0
|
|
||||||
self._status = "Stirring Stopped"
|
|
||||||
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"message": "Stirring stopped successfully"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ==================== 内部控制方法 ====================
|
|
||||||
|
|
||||||
def _operation_loop(self):
|
|
||||||
"""操作主循环"""
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
current_time = datetime.now()
|
|
||||||
|
|
||||||
with self._thread_lock: # 添加锁保护
|
|
||||||
if self._stir_state == "Running":
|
|
||||||
# 实际搅拌逻辑
|
|
||||||
speed_diff = self._target_stir_speed - self._stir_speed
|
|
||||||
if abs(speed_diff) > 0.1:
|
|
||||||
adjustment = speed_diff * 0.1
|
|
||||||
self._stir_speed += adjustment
|
|
||||||
else:
|
|
||||||
self._stir_speed = self._target_stir_speed
|
|
||||||
|
|
||||||
# 更新进度
|
|
||||||
if self._running:
|
|
||||||
if self._stir_time > 0: # 定时搅拌模式
|
|
||||||
elapsed = (current_time - self._start_time).total_seconds()
|
|
||||||
if elapsed >= self._stir_time + self._settling_time:
|
|
||||||
self._running = False
|
|
||||||
self._stir_state = "Stopped"
|
|
||||||
self._target_stir_speed = 0.0
|
|
||||||
self._stir_speed = 0.0
|
|
||||||
self._status = "Stirring Complete"
|
|
||||||
elif elapsed >= self._stir_time:
|
|
||||||
self._status = "Settling"
|
|
||||||
else: # 连续搅拌模式
|
|
||||||
self._status = "Stirring"
|
|
||||||
else:
|
|
||||||
# 停止状态下慢慢降低速度
|
|
||||||
if self._stir_speed > 0:
|
|
||||||
self._stir_speed = max(0, self._stir_speed - 20.0)
|
|
||||||
|
|
||||||
time.sleep(0.1)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error in operation loop: {str(e)}") # 添加错误输出
|
|
||||||
self._status = f"Error: {str(e)}"
|
|
||||||
time.sleep(1.0) # 错误发生时等待较长时间
|
|
||||||
|
|
||||||
def get_status_info(self) -> dict:
|
|
||||||
"""获取设备状态信息"""
|
|
||||||
return {
|
|
||||||
"status": self._status,
|
|
||||||
"vessel": self._vessel,
|
|
||||||
"purpose": self._purpose,
|
|
||||||
"stir_speed": self._stir_speed,
|
|
||||||
"target_stir_speed": self._target_stir_speed,
|
|
||||||
"stir_state": self._stir_state,
|
|
||||||
"stir_time": self._stir_time, # 添加
|
|
||||||
"settling_time": self._settling_time, # 添加
|
|
||||||
"progress": self.progress,
|
|
||||||
"max_stir_speed": self._max_stir_speed
|
|
||||||
}
|
|
||||||
@@ -1,410 +0,0 @@
|
|||||||
import time
|
|
||||||
import threading
|
|
||||||
|
|
||||||
|
|
||||||
class MockVacuum:
|
|
||||||
"""
|
|
||||||
模拟真空泵设备类
|
|
||||||
|
|
||||||
这个类模拟了一个实验室真空泵的行为,包括真空度控制、
|
|
||||||
压力监测、运行状态管理等功能。参考了现有的 VacuumPumpMock 实现。
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, port: str = "MOCK"):
|
|
||||||
"""
|
|
||||||
初始化MockVacuum实例
|
|
||||||
|
|
||||||
Args:
|
|
||||||
port (str): 设备端口,默认为"MOCK"表示模拟设备
|
|
||||||
"""
|
|
||||||
self.port = port
|
|
||||||
|
|
||||||
# 设备基本状态属性
|
|
||||||
self._status: str = "Idle" # 设备状态:Idle, Running, Error, Stopped
|
|
||||||
self._power_state: str = "Off" # 电源状态:On, Off
|
|
||||||
self._pump_state: str = "Stopped" # 泵运行状态:Running, Stopped, Paused
|
|
||||||
|
|
||||||
# 真空相关属性
|
|
||||||
self._vacuum_level: float = 1013.25 # 当前真空度 (mbar) - 大气压开始
|
|
||||||
self._target_vacuum: float = 50.0 # 目标真空度 (mbar)
|
|
||||||
self._min_vacuum: float = 1.0 # 最小真空度 (mbar)
|
|
||||||
self._max_vacuum: float = 1013.25 # 最大真空度 (mbar) - 大气压
|
|
||||||
|
|
||||||
# 泵性能相关属性
|
|
||||||
self._pump_speed: float = 0.0 # 泵速 (L/s)
|
|
||||||
self._max_pump_speed: float = 100.0 # 最大泵速 (L/s)
|
|
||||||
self._pump_efficiency: float = 95.0 # 泵效率百分比
|
|
||||||
|
|
||||||
# 运行控制线程
|
|
||||||
self._vacuum_thread = None
|
|
||||||
self._running = False
|
|
||||||
self._thread_lock = threading.Lock()
|
|
||||||
|
|
||||||
# ==================== 状态属性 ====================
|
|
||||||
# 这些属性会被Uni-Lab系统自动识别并定时对外广播
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status(self) -> str:
|
|
||||||
"""
|
|
||||||
设备状态 - 会被自动识别的设备属性
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 当前设备状态 (Idle, Running, Error, Stopped)
|
|
||||||
"""
|
|
||||||
return self._status
|
|
||||||
|
|
||||||
@property
|
|
||||||
def power_state(self) -> str:
|
|
||||||
"""
|
|
||||||
电源状态
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 电源状态 (On, Off)
|
|
||||||
"""
|
|
||||||
return self._power_state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def pump_state(self) -> str:
|
|
||||||
"""
|
|
||||||
泵运行状态
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 泵状态 (Running, Stopped, Paused)
|
|
||||||
"""
|
|
||||||
return self._pump_state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def vacuum_level(self) -> float:
|
|
||||||
"""
|
|
||||||
当前真空度
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 当前真空度 (mbar)
|
|
||||||
"""
|
|
||||||
return self._vacuum_level
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_vacuum(self) -> float:
|
|
||||||
"""
|
|
||||||
目标真空度
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 目标真空度 (mbar)
|
|
||||||
"""
|
|
||||||
return self._target_vacuum
|
|
||||||
|
|
||||||
@property
|
|
||||||
def pump_speed(self) -> float:
|
|
||||||
"""
|
|
||||||
泵速
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 泵速 (L/s)
|
|
||||||
"""
|
|
||||||
return self._pump_speed
|
|
||||||
|
|
||||||
@property
|
|
||||||
def pump_efficiency(self) -> float:
|
|
||||||
"""
|
|
||||||
泵效率
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 泵效率百分比
|
|
||||||
"""
|
|
||||||
return self._pump_efficiency
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_pump_speed(self) -> float:
|
|
||||||
"""
|
|
||||||
最大泵速
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 最大泵速 (L/s)
|
|
||||||
"""
|
|
||||||
return self._max_pump_speed
|
|
||||||
|
|
||||||
# ==================== 设备控制方法 ====================
|
|
||||||
# 这些方法需要在注册表中添加,会作为ActionServer接受控制指令
|
|
||||||
|
|
||||||
def power_control(self, power_state: str = "On") -> str:
|
|
||||||
"""
|
|
||||||
电源控制方法
|
|
||||||
|
|
||||||
Args:
|
|
||||||
power_state (str): 电源状态,可选值:"On", "Off"
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
if power_state not in ["On", "Off"]:
|
|
||||||
self._status = "Error: Invalid power state"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._power_state = power_state
|
|
||||||
|
|
||||||
if power_state == "On":
|
|
||||||
self._status = "Power On"
|
|
||||||
self._start_vacuum_operation()
|
|
||||||
else:
|
|
||||||
self._status = "Power Off"
|
|
||||||
self.stop_vacuum()
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def set_vacuum_level(self, vacuum_level: float) -> str:
|
|
||||||
"""
|
|
||||||
设置目标真空度
|
|
||||||
|
|
||||||
Args:
|
|
||||||
vacuum_level (float): 目标真空度 (mbar)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
vacuum_level = float(vacuum_level)
|
|
||||||
except ValueError:
|
|
||||||
self._status = "Error: Invalid vacuum level"
|
|
||||||
return "Error"
|
|
||||||
if self._power_state != "On":
|
|
||||||
self._status = "Error: Power Off"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
if vacuum_level < self._min_vacuum or vacuum_level > self._max_vacuum:
|
|
||||||
self._status = f"Error: Vacuum level out of range ({self._min_vacuum}-{self._max_vacuum})"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._target_vacuum = vacuum_level
|
|
||||||
self._status = "Setting Vacuum Level"
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def start_vacuum(self) -> str:
|
|
||||||
"""
|
|
||||||
启动真空泵
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
if self._power_state != "On":
|
|
||||||
self._status = "Error: Power Off"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._pump_state = "Running"
|
|
||||||
self._status = "Starting Vacuum Pump"
|
|
||||||
self._start_vacuum_operation()
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def stop_vacuum(self) -> str:
|
|
||||||
"""
|
|
||||||
停止真空泵
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
self._pump_state = "Stopped"
|
|
||||||
self._status = "Stopping Vacuum Pump"
|
|
||||||
self._stop_vacuum_operation()
|
|
||||||
self._pump_speed = 0.0
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def pause_vacuum(self) -> str:
|
|
||||||
"""
|
|
||||||
暂停真空泵
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
if self._pump_state != "Running":
|
|
||||||
self._status = "Error: Pump not running"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._pump_state = "Paused"
|
|
||||||
self._status = "Vacuum Pump Paused"
|
|
||||||
self._stop_vacuum_operation()
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def resume_vacuum(self) -> str:
|
|
||||||
"""
|
|
||||||
恢复真空泵运行
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
if self._pump_state != "Paused":
|
|
||||||
self._status = "Error: Pump not paused"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
if self._power_state != "On":
|
|
||||||
self._status = "Error: Power Off"
|
|
||||||
return "Error"
|
|
||||||
|
|
||||||
self._pump_state = "Running"
|
|
||||||
self._status = "Resuming Vacuum Pump"
|
|
||||||
self._start_vacuum_operation()
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def vent_to_atmosphere(self) -> str:
|
|
||||||
"""
|
|
||||||
通大气 - 将真空度恢复到大气压
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
self._target_vacuum = self._max_vacuum # 设置为大气压
|
|
||||||
self._status = "Venting to Atmosphere"
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
def emergency_stop(self) -> str:
|
|
||||||
"""
|
|
||||||
紧急停止
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 操作结果状态 ("Success", "Error")
|
|
||||||
"""
|
|
||||||
self._status = "Emergency Stop"
|
|
||||||
self._pump_state = "Stopped"
|
|
||||||
self._stop_vacuum_operation()
|
|
||||||
self._pump_speed = 0.0
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
|
|
||||||
# ==================== 内部控制方法 ====================
|
|
||||||
|
|
||||||
def _start_vacuum_operation(self):
|
|
||||||
"""
|
|
||||||
启动真空操作线程
|
|
||||||
|
|
||||||
这个方法启动一个后台线程来模拟真空泵的实际运行过程。
|
|
||||||
"""
|
|
||||||
with self._thread_lock:
|
|
||||||
if not self._running and self._power_state == "On":
|
|
||||||
self._running = True
|
|
||||||
self._vacuum_thread = threading.Thread(target=self._vacuum_operation_loop)
|
|
||||||
self._vacuum_thread.daemon = True
|
|
||||||
self._vacuum_thread.start()
|
|
||||||
|
|
||||||
def _stop_vacuum_operation(self):
|
|
||||||
"""
|
|
||||||
停止真空操作线程
|
|
||||||
|
|
||||||
安全地停止后台运行线程并等待其完成。
|
|
||||||
"""
|
|
||||||
with self._thread_lock:
|
|
||||||
self._running = False
|
|
||||||
if self._vacuum_thread and self._vacuum_thread.is_alive():
|
|
||||||
self._vacuum_thread.join(timeout=2.0)
|
|
||||||
|
|
||||||
def _vacuum_operation_loop(self):
|
|
||||||
"""
|
|
||||||
真空操作主循环
|
|
||||||
|
|
||||||
这个方法在后台线程中运行,模拟真空泵的工作过程:
|
|
||||||
1. 检查电源状态和运行状态
|
|
||||||
2. 如果泵状态为 "Running",根据目标真空调整泵速和真空度
|
|
||||||
3. 否则等待
|
|
||||||
"""
|
|
||||||
while self._running and self._power_state == "On":
|
|
||||||
try:
|
|
||||||
with self._thread_lock:
|
|
||||||
# 只有泵状态为 Running 时才进行更新
|
|
||||||
if self._pump_state == "Running":
|
|
||||||
vacuum_diff = self._vacuum_level - self._target_vacuum
|
|
||||||
|
|
||||||
if abs(vacuum_diff) < 1.0: # 真空度接近目标值
|
|
||||||
self._status = "At Target Vacuum"
|
|
||||||
self._pump_speed = self._max_pump_speed * 0.2 # 维持真空的最小泵速
|
|
||||||
elif vacuum_diff > 0: # 需要抽真空(降低压力)
|
|
||||||
self._status = "Pumping Down"
|
|
||||||
if vacuum_diff > 500:
|
|
||||||
self._pump_speed = self._max_pump_speed
|
|
||||||
elif vacuum_diff > 100:
|
|
||||||
self._pump_speed = self._max_pump_speed * 0.8
|
|
||||||
elif vacuum_diff > 50:
|
|
||||||
self._pump_speed = self._max_pump_speed * 0.6
|
|
||||||
else:
|
|
||||||
self._pump_speed = self._max_pump_speed * 0.4
|
|
||||||
|
|
||||||
# 根据泵速和效率计算真空降幅
|
|
||||||
pump_rate = (self._pump_speed / self._max_pump_speed) * self._pump_efficiency / 100.0
|
|
||||||
vacuum_reduction = pump_rate * 10.0 # 每秒最大降低10 mbar
|
|
||||||
self._vacuum_level = max(self._target_vacuum, self._vacuum_level - vacuum_reduction)
|
|
||||||
else: # 目标真空度高于当前值,需要通气
|
|
||||||
self._status = "Venting"
|
|
||||||
self._pump_speed = 0.0
|
|
||||||
self._vacuum_level = min(self._target_vacuum, self._vacuum_level + 5.0)
|
|
||||||
|
|
||||||
# 限制真空度范围
|
|
||||||
self._vacuum_level = max(self._min_vacuum, min(self._max_vacuum, self._vacuum_level))
|
|
||||||
else:
|
|
||||||
# 当泵状态不是 Running 时,可保持原状态
|
|
||||||
self._status = "Vacuum Pump Not Running"
|
|
||||||
# 释放锁后等待1秒钟
|
|
||||||
time.sleep(1.0)
|
|
||||||
except Exception as e:
|
|
||||||
with self._thread_lock:
|
|
||||||
self._status = f"Error in vacuum operation: {str(e)}"
|
|
||||||
break
|
|
||||||
|
|
||||||
# 循环结束后的清理工作
|
|
||||||
if self._pump_state == "Running":
|
|
||||||
self._status = "Idle"
|
|
||||||
# 停止泵后,真空度逐渐回升到大气压
|
|
||||||
while self._vacuum_level < self._max_vacuum * 0.9:
|
|
||||||
with self._thread_lock:
|
|
||||||
self._vacuum_level += 2.0
|
|
||||||
time.sleep(0.1)
|
|
||||||
|
|
||||||
def get_status_info(self) -> dict:
|
|
||||||
"""
|
|
||||||
获取完整的设备状态信息
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: 包含所有设备状态的字典
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
"status": self._status,
|
|
||||||
"power_state": self._power_state,
|
|
||||||
"pump_state": self._pump_state,
|
|
||||||
"vacuum_level": self._vacuum_level,
|
|
||||||
"target_vacuum": self._target_vacuum,
|
|
||||||
"pump_speed": self._pump_speed,
|
|
||||||
"pump_efficiency": self._pump_efficiency,
|
|
||||||
"max_pump_speed": self._max_pump_speed,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# 用于测试的主函数
|
|
||||||
if __name__ == "__main__":
|
|
||||||
vacuum = MockVacuum()
|
|
||||||
|
|
||||||
# 测试基本功能
|
|
||||||
print("启动真空泵测试...")
|
|
||||||
vacuum.power_control("On")
|
|
||||||
print(f"初始状态: {vacuum.get_status_info()}")
|
|
||||||
|
|
||||||
# 设置目标真空度并启动
|
|
||||||
vacuum.set_vacuum_level(10.0) # 设置为10mbar
|
|
||||||
vacuum.start_vacuum()
|
|
||||||
|
|
||||||
# 模拟运行15秒
|
|
||||||
for i in range(15):
|
|
||||||
time.sleep(1)
|
|
||||||
print(
|
|
||||||
f"第{i+1}秒: 真空度={vacuum.vacuum_level:.1f}mbar, 泵速={vacuum.pump_speed:.1f}L/s, 状态={vacuum.status}"
|
|
||||||
)
|
|
||||||
# 测试通大气
|
|
||||||
print("测试通大气...")
|
|
||||||
vacuum.vent_to_atmosphere()
|
|
||||||
|
|
||||||
# 继续运行5秒观察通大气过程
|
|
||||||
for i in range(5):
|
|
||||||
time.sleep(1)
|
|
||||||
print(f"通大气第{i+1}秒: 真空度={vacuum.vacuum_level:.1f}mbar, 状态={vacuum.status}")
|
|
||||||
|
|
||||||
vacuum.emergency_stop()
|
|
||||||
print("测试完成")
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
serial:
|
serial:
|
||||||
category:
|
category:
|
||||||
- serial
|
- communication_devices
|
||||||
class:
|
class:
|
||||||
action_value_mappings:
|
action_value_mappings:
|
||||||
auto-handle_serial_request:
|
auto-handle_serial_request:
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
camera:
|
camera.USB:
|
||||||
category:
|
category:
|
||||||
- camera
|
- camera
|
||||||
class:
|
class:
|
||||||
|
|||||||
404
unilabos/registry/devices/characterization_chromatic.yaml
Normal file
404
unilabos/registry/devices/characterization_chromatic.yaml
Normal file
@@ -0,0 +1,404 @@
|
|||||||
|
hplc.agilent:
|
||||||
|
category:
|
||||||
|
- characterization_chromatic
|
||||||
|
class:
|
||||||
|
action_value_mappings:
|
||||||
|
auto-check_status:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default: {}
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: 检查安捷伦HPLC设备状态的函数。用于监控设备的运行状态、连接状态、错误信息等关键指标。该函数定期查询设备状态,确保系统稳定运行,及时发现和报告设备异常。适用于自动化流程中的设备监控、故障诊断、系统维护等场景。
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties: {}
|
||||||
|
required: []
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: check_status参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-extract_data_from_txt:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
file_path: null
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: 从文本文件中提取分析数据的函数。用于解析安捷伦HPLC生成的结果文件,提取峰面积、保留时间、浓度等关键分析数据。支持多种文件格式的自动识别和数据结构化处理,为后续数据分析和报告生成提供标准化的数据格式。适用于批量数据处理、结果验证、质量控制等分析工作流程。
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
file_path:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- file_path
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: extract_data_from_txt参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-start_sequence:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
params: null
|
||||||
|
resource: null
|
||||||
|
wf_name: null
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: 启动安捷伦HPLC分析序列的函数。用于执行预定义的分析方法序列,包括样品进样、色谱分离、检测等完整的分析流程。支持参数配置、资源分配、工作流程管理等功能,实现全自动的样品分析。适用于批量样品处理、标准化分析、质量检测等需要连续自动分析的应用场景。
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
params:
|
||||||
|
type: string
|
||||||
|
resource:
|
||||||
|
type: object
|
||||||
|
wf_name:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- wf_name
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: start_sequence参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-try_close_sub_device:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
device_name: null
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: 尝试关闭HPLC子设备的函数。用于安全地关闭泵、检测器、进样器等各个子模块,确保设备正常断开连接并保护硬件安全。该函数提供错误处理和状态确认机制,避免强制关闭可能造成的设备损坏。适用于设备维护、系统重启、紧急停机等需要安全关闭设备的场景。
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
device_name:
|
||||||
|
type: string
|
||||||
|
required: []
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: try_close_sub_device参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-try_open_sub_device:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
device_name: null
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: 尝试打开HPLC子设备的函数。用于初始化和连接泵、检测器、进样器等各个子模块,建立设备通信并进行自检。该函数提供连接验证和错误恢复机制,确保子设备正常启动并准备就绪。适用于设备初始化、系统启动、设备重连等需要建立设备连接的场景。
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
device_name:
|
||||||
|
type: string
|
||||||
|
required: []
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: try_open_sub_device参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
execute_command_from_outer:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
command: command
|
||||||
|
goal_default:
|
||||||
|
command: ''
|
||||||
|
handles: []
|
||||||
|
result:
|
||||||
|
success: success
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback:
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
title: SendCmd_Feedback
|
||||||
|
type: object
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
command:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- command
|
||||||
|
title: SendCmd_Goal
|
||||||
|
type: object
|
||||||
|
result:
|
||||||
|
properties:
|
||||||
|
return_info:
|
||||||
|
type: string
|
||||||
|
success:
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- return_info
|
||||||
|
- success
|
||||||
|
title: SendCmd_Result
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: SendCmd
|
||||||
|
type: object
|
||||||
|
type: SendCmd
|
||||||
|
module: unilabos.devices.hplc.AgilentHPLC:HPLCDriver
|
||||||
|
status_types:
|
||||||
|
could_run: bool
|
||||||
|
data_file: list
|
||||||
|
device_status: str
|
||||||
|
driver_init_ok: bool
|
||||||
|
finish_status: str
|
||||||
|
is_running: bool
|
||||||
|
status_text: str
|
||||||
|
success: bool
|
||||||
|
type: python
|
||||||
|
config_info: []
|
||||||
|
description: 安捷伦高效液相色谱(HPLC)分析设备,用于复杂化合物的分离、检测和定量分析。该设备通过UI自动化技术控制安捷伦ChemStation软件,实现全自动的样品分析流程。具备序列启动、设备状态监控、数据文件提取、结果处理等功能。支持多样品批量处理和实时状态反馈,适用于药物分析、环境检测、食品安全、化学研究等需要高精度色谱分析的实验室应用。
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema:
|
||||||
|
config:
|
||||||
|
properties:
|
||||||
|
driver_debug:
|
||||||
|
default: false
|
||||||
|
type: string
|
||||||
|
required: []
|
||||||
|
type: object
|
||||||
|
data:
|
||||||
|
properties:
|
||||||
|
could_run:
|
||||||
|
type: boolean
|
||||||
|
data_file:
|
||||||
|
type: array
|
||||||
|
device_status:
|
||||||
|
type: string
|
||||||
|
driver_init_ok:
|
||||||
|
type: boolean
|
||||||
|
finish_status:
|
||||||
|
type: string
|
||||||
|
is_running:
|
||||||
|
type: boolean
|
||||||
|
status_text:
|
||||||
|
type: string
|
||||||
|
success:
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- status_text
|
||||||
|
- device_status
|
||||||
|
- could_run
|
||||||
|
- driver_init_ok
|
||||||
|
- is_running
|
||||||
|
- success
|
||||||
|
- finish_status
|
||||||
|
- data_file
|
||||||
|
type: object
|
||||||
|
version: 1.0.0
|
||||||
|
hplc.agilent-zhida:
|
||||||
|
category:
|
||||||
|
- characterization_chromatic
|
||||||
|
class:
|
||||||
|
action_value_mappings:
|
||||||
|
abort:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default: {}
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback:
|
||||||
|
properties: {}
|
||||||
|
required: []
|
||||||
|
title: EmptyIn_Feedback
|
||||||
|
type: object
|
||||||
|
goal:
|
||||||
|
properties: {}
|
||||||
|
required: []
|
||||||
|
title: EmptyIn_Goal
|
||||||
|
type: object
|
||||||
|
result:
|
||||||
|
properties:
|
||||||
|
return_info:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- return_info
|
||||||
|
title: EmptyIn_Result
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: EmptyIn
|
||||||
|
type: object
|
||||||
|
type: EmptyIn
|
||||||
|
auto-close:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default: {}
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: HPLC设备连接关闭函数。安全地断开与智达HPLC设备的TCP socket连接,释放网络资源。该函数确保连接的正确关闭,避免网络资源泄露。通常在设备使用完毕或系统关闭时调用。
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties: {}
|
||||||
|
required: []
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: close参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-connect:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default: {}
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: HPLC设备连接建立函数。与智达HPLC设备建立TCP socket通信连接,配置通信超时参数。该函数是设备使用前的必要步骤,建立成功后可进行状态查询、方法获取、任务启动等操作。连接失败时会抛出异常。
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties: {}
|
||||||
|
required: []
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: connect参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
get_methods:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default: {}
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback:
|
||||||
|
properties: {}
|
||||||
|
required: []
|
||||||
|
title: EmptyIn_Feedback
|
||||||
|
type: object
|
||||||
|
goal:
|
||||||
|
properties: {}
|
||||||
|
required: []
|
||||||
|
title: EmptyIn_Goal
|
||||||
|
type: object
|
||||||
|
result:
|
||||||
|
properties:
|
||||||
|
return_info:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- return_info
|
||||||
|
title: EmptyIn_Result
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: EmptyIn
|
||||||
|
type: object
|
||||||
|
type: EmptyIn
|
||||||
|
start:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
string: string
|
||||||
|
goal_default:
|
||||||
|
string: ''
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback:
|
||||||
|
properties: {}
|
||||||
|
required: []
|
||||||
|
title: StrSingleInput_Feedback
|
||||||
|
type: object
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
string:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- string
|
||||||
|
title: StrSingleInput_Goal
|
||||||
|
type: object
|
||||||
|
result:
|
||||||
|
properties:
|
||||||
|
return_info:
|
||||||
|
type: string
|
||||||
|
success:
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- return_info
|
||||||
|
- success
|
||||||
|
title: StrSingleInput_Result
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: StrSingleInput
|
||||||
|
type: object
|
||||||
|
type: StrSingleInput
|
||||||
|
module: unilabos.devices.zhida_hplc.zhida:ZhidaClient
|
||||||
|
status_types:
|
||||||
|
methods: dict
|
||||||
|
status: dict
|
||||||
|
type: python
|
||||||
|
config_info: []
|
||||||
|
description: 智达高效液相色谱(HPLC)分析设备,用于实验室样品的分离、检测和定量分析。该设备通过TCP socket与HPLC控制系统通信,支持远程控制和状态监控。具备自动进样、梯度洗脱、多检测器数据采集等功能,可执行复杂的色谱分析方法。适用于化学分析、药物检测、环境监测、生物样品分析等需要高精度分离分析的实验室应用场景。
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema:
|
||||||
|
config:
|
||||||
|
properties:
|
||||||
|
host:
|
||||||
|
default: 192.168.1.47
|
||||||
|
type: string
|
||||||
|
port:
|
||||||
|
default: 5792
|
||||||
|
type: string
|
||||||
|
timeout:
|
||||||
|
default: 10.0
|
||||||
|
type: string
|
||||||
|
required: []
|
||||||
|
type: object
|
||||||
|
data:
|
||||||
|
properties:
|
||||||
|
methods:
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
- methods
|
||||||
|
type: object
|
||||||
|
version: 1.0.0
|
||||||
@@ -1,225 +1,4 @@
|
|||||||
hplc.agilent:
|
raman.home_made:
|
||||||
category:
|
|
||||||
- characterization_optic
|
|
||||||
class:
|
|
||||||
action_value_mappings:
|
|
||||||
auto-check_status:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default: {}
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: 检查安捷伦HPLC设备状态的函数。用于监控设备的运行状态、连接状态、错误信息等关键指标。该函数定期查询设备状态,确保系统稳定运行,及时发现和报告设备异常。适用于自动化流程中的设备监控、故障诊断、系统维护等场景。
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: check_status参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-extract_data_from_txt:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
file_path: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: 从文本文件中提取分析数据的函数。用于解析安捷伦HPLC生成的结果文件,提取峰面积、保留时间、浓度等关键分析数据。支持多种文件格式的自动识别和数据结构化处理,为后续数据分析和报告生成提供标准化的数据格式。适用于批量数据处理、结果验证、质量控制等分析工作流程。
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
file_path:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- file_path
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: extract_data_from_txt参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-start_sequence:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
params: null
|
|
||||||
resource: null
|
|
||||||
wf_name: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: 启动安捷伦HPLC分析序列的函数。用于执行预定义的分析方法序列,包括样品进样、色谱分离、检测等完整的分析流程。支持参数配置、资源分配、工作流程管理等功能,实现全自动的样品分析。适用于批量样品处理、标准化分析、质量检测等需要连续自动分析的应用场景。
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
params:
|
|
||||||
type: string
|
|
||||||
resource:
|
|
||||||
type: object
|
|
||||||
wf_name:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- wf_name
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: start_sequence参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-try_close_sub_device:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
device_name: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: 尝试关闭HPLC子设备的函数。用于安全地关闭泵、检测器、进样器等各个子模块,确保设备正常断开连接并保护硬件安全。该函数提供错误处理和状态确认机制,避免强制关闭可能造成的设备损坏。适用于设备维护、系统重启、紧急停机等需要安全关闭设备的场景。
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
device_name:
|
|
||||||
type: string
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: try_close_sub_device参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-try_open_sub_device:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
device_name: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: 尝试打开HPLC子设备的函数。用于初始化和连接泵、检测器、进样器等各个子模块,建立设备通信并进行自检。该函数提供连接验证和错误恢复机制,确保子设备正常启动并准备就绪。适用于设备初始化、系统启动、设备重连等需要建立设备连接的场景。
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
device_name:
|
|
||||||
type: string
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: try_open_sub_device参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
execute_command_from_outer:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
command: command
|
|
||||||
goal_default:
|
|
||||||
command: ''
|
|
||||||
handles: []
|
|
||||||
result:
|
|
||||||
success: success
|
|
||||||
schema:
|
|
||||||
description: ''
|
|
||||||
properties:
|
|
||||||
feedback:
|
|
||||||
properties:
|
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- status
|
|
||||||
title: SendCmd_Feedback
|
|
||||||
type: object
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
command:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- command
|
|
||||||
title: SendCmd_Goal
|
|
||||||
type: object
|
|
||||||
result:
|
|
||||||
properties:
|
|
||||||
return_info:
|
|
||||||
type: string
|
|
||||||
success:
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- return_info
|
|
||||||
- success
|
|
||||||
title: SendCmd_Result
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: SendCmd
|
|
||||||
type: object
|
|
||||||
type: SendCmd
|
|
||||||
module: unilabos.devices.hplc.AgilentHPLC:HPLCDriver
|
|
||||||
status_types:
|
|
||||||
could_run: bool
|
|
||||||
data_file: list
|
|
||||||
device_status: str
|
|
||||||
driver_init_ok: bool
|
|
||||||
finish_status: str
|
|
||||||
is_running: bool
|
|
||||||
status_text: str
|
|
||||||
success: bool
|
|
||||||
type: python
|
|
||||||
config_info: []
|
|
||||||
description: 安捷伦高效液相色谱(HPLC)分析设备,用于复杂化合物的分离、检测和定量分析。该设备通过UI自动化技术控制安捷伦ChemStation软件,实现全自动的样品分析流程。具备序列启动、设备状态监控、数据文件提取、结果处理等功能。支持多样品批量处理和实时状态反馈,适用于药物分析、环境检测、食品安全、化学研究等需要高精度色谱分析的实验室应用。
|
|
||||||
handles: []
|
|
||||||
icon: ''
|
|
||||||
init_param_schema:
|
|
||||||
config:
|
|
||||||
properties:
|
|
||||||
driver_debug:
|
|
||||||
default: false
|
|
||||||
type: string
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
data:
|
|
||||||
properties:
|
|
||||||
could_run:
|
|
||||||
type: boolean
|
|
||||||
data_file:
|
|
||||||
type: array
|
|
||||||
device_status:
|
|
||||||
type: string
|
|
||||||
driver_init_ok:
|
|
||||||
type: boolean
|
|
||||||
finish_status:
|
|
||||||
type: string
|
|
||||||
is_running:
|
|
||||||
type: boolean
|
|
||||||
status_text:
|
|
||||||
type: string
|
|
||||||
success:
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- status_text
|
|
||||||
- device_status
|
|
||||||
- could_run
|
|
||||||
- driver_init_ok
|
|
||||||
- is_running
|
|
||||||
- success
|
|
||||||
- finish_status
|
|
||||||
- data_file
|
|
||||||
type: object
|
|
||||||
version: 1.0.0
|
|
||||||
raman_home_made:
|
|
||||||
category:
|
category:
|
||||||
- characterization_optic
|
- characterization_optic
|
||||||
class:
|
class:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
gas_source.mock:
|
gas_source.mock:
|
||||||
category:
|
category:
|
||||||
- vacuum_and_purge
|
- gas_handler
|
||||||
class:
|
class:
|
||||||
action_value_mappings:
|
action_value_mappings:
|
||||||
auto-is_closed:
|
auto-is_closed:
|
||||||
@@ -180,6 +180,7 @@ gas_source.mock:
|
|||||||
vacuum_pump.mock:
|
vacuum_pump.mock:
|
||||||
category:
|
category:
|
||||||
- vacuum_and_purge
|
- vacuum_and_purge
|
||||||
|
- gas_handler
|
||||||
class:
|
class:
|
||||||
action_value_mappings:
|
action_value_mappings:
|
||||||
auto-is_closed:
|
auto-is_closed:
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,704 +0,0 @@
|
|||||||
moveit.arm_slider:
|
|
||||||
category:
|
|
||||||
- moveit_config
|
|
||||||
class:
|
|
||||||
action_value_mappings:
|
|
||||||
auto-check_tf_update_actions:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default: {}
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: check_tf_update_actions的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: check_tf_update_actions参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-moveit_joint_task:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
joint_names: null
|
|
||||||
joint_positions: null
|
|
||||||
move_group: null
|
|
||||||
retry: 10
|
|
||||||
speed: 1
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: moveit_joint_task的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
joint_names:
|
|
||||||
type: string
|
|
||||||
joint_positions:
|
|
||||||
type: string
|
|
||||||
move_group:
|
|
||||||
type: string
|
|
||||||
retry:
|
|
||||||
default: 10
|
|
||||||
type: string
|
|
||||||
speed:
|
|
||||||
default: 1
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- move_group
|
|
||||||
- joint_positions
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: moveit_joint_task参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-moveit_task:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
cartesian: false
|
|
||||||
move_group: null
|
|
||||||
offsets:
|
|
||||||
- 0
|
|
||||||
- 0
|
|
||||||
- 0
|
|
||||||
position: null
|
|
||||||
quaternion: null
|
|
||||||
retry: 10
|
|
||||||
speed: 1
|
|
||||||
target_link: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: moveit_task的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
cartesian:
|
|
||||||
default: false
|
|
||||||
type: string
|
|
||||||
move_group:
|
|
||||||
type: string
|
|
||||||
offsets:
|
|
||||||
default:
|
|
||||||
- 0
|
|
||||||
- 0
|
|
||||||
- 0
|
|
||||||
type: string
|
|
||||||
position:
|
|
||||||
type: string
|
|
||||||
quaternion:
|
|
||||||
type: string
|
|
||||||
retry:
|
|
||||||
default: 10
|
|
||||||
type: string
|
|
||||||
speed:
|
|
||||||
default: 1
|
|
||||||
type: string
|
|
||||||
target_link:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- move_group
|
|
||||||
- position
|
|
||||||
- quaternion
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: moveit_task参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-post_init:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
ros_node: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: post_init的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
ros_node:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- ros_node
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: post_init参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-resource_manager:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
parent_link: null
|
|
||||||
resource: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: resource_manager的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
parent_link:
|
|
||||||
type: string
|
|
||||||
resource:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- resource
|
|
||||||
- parent_link
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: resource_manager参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-wait_for_resource_action:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default: {}
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: wait_for_resource_action的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: wait_for_resource_action参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
pick_and_place:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
command: command
|
|
||||||
goal_default:
|
|
||||||
command: ''
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: ''
|
|
||||||
properties:
|
|
||||||
feedback:
|
|
||||||
properties:
|
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- status
|
|
||||||
title: SendCmd_Feedback
|
|
||||||
type: object
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
command:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- command
|
|
||||||
title: SendCmd_Goal
|
|
||||||
type: object
|
|
||||||
result:
|
|
||||||
properties:
|
|
||||||
return_info:
|
|
||||||
type: string
|
|
||||||
success:
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- return_info
|
|
||||||
- success
|
|
||||||
title: SendCmd_Result
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: SendCmd
|
|
||||||
type: object
|
|
||||||
type: SendCmd
|
|
||||||
set_position:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
command: command
|
|
||||||
goal_default:
|
|
||||||
command: ''
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: ''
|
|
||||||
properties:
|
|
||||||
feedback:
|
|
||||||
properties:
|
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- status
|
|
||||||
title: SendCmd_Feedback
|
|
||||||
type: object
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
command:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- command
|
|
||||||
title: SendCmd_Goal
|
|
||||||
type: object
|
|
||||||
result:
|
|
||||||
properties:
|
|
||||||
return_info:
|
|
||||||
type: string
|
|
||||||
success:
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- return_info
|
|
||||||
- success
|
|
||||||
title: SendCmd_Result
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: SendCmd
|
|
||||||
type: object
|
|
||||||
type: SendCmd
|
|
||||||
set_status:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
command: command
|
|
||||||
goal_default:
|
|
||||||
command: ''
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: ''
|
|
||||||
properties:
|
|
||||||
feedback:
|
|
||||||
properties:
|
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- status
|
|
||||||
title: SendCmd_Feedback
|
|
||||||
type: object
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
command:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- command
|
|
||||||
title: SendCmd_Goal
|
|
||||||
type: object
|
|
||||||
result:
|
|
||||||
properties:
|
|
||||||
return_info:
|
|
||||||
type: string
|
|
||||||
success:
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- return_info
|
|
||||||
- success
|
|
||||||
title: SendCmd_Result
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: SendCmd
|
|
||||||
type: object
|
|
||||||
type: SendCmd
|
|
||||||
module: unilabos.devices.ros_dev.moveit_interface:MoveitInterface
|
|
||||||
status_types: {}
|
|
||||||
type: python
|
|
||||||
config_info: []
|
|
||||||
description: 机械臂与滑块运动系统,基于MoveIt2运动规划框架的多自由度机械臂控制设备。该系统集成机械臂和线性滑块,通过ROS2和MoveIt2实现精确的轨迹规划和协调运动控制。支持笛卡尔空间和关节空间的运动规划、碰撞检测、逆运动学求解等功能。适用于复杂的pick-and-place操作、精密装配、多工位协作等需要高精度多轴协调运动的实验室自动化应用。
|
|
||||||
handles: []
|
|
||||||
icon: ''
|
|
||||||
init_param_schema:
|
|
||||||
config:
|
|
||||||
properties:
|
|
||||||
device_config:
|
|
||||||
type: string
|
|
||||||
joint_poses:
|
|
||||||
type: string
|
|
||||||
moveit_type:
|
|
||||||
type: string
|
|
||||||
rotation:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- moveit_type
|
|
||||||
- joint_poses
|
|
||||||
type: object
|
|
||||||
data:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
model:
|
|
||||||
mesh: arm_slider
|
|
||||||
type: device
|
|
||||||
version: 1.0.0
|
|
||||||
moveit.toyo_xyz:
|
|
||||||
category:
|
|
||||||
- moveit_config
|
|
||||||
class:
|
|
||||||
action_value_mappings:
|
|
||||||
auto-check_tf_update_actions:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default: {}
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: check_tf_update_actions的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: check_tf_update_actions参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-moveit_joint_task:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
joint_names: null
|
|
||||||
joint_positions: null
|
|
||||||
move_group: null
|
|
||||||
retry: 10
|
|
||||||
speed: 1
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: moveit_joint_task的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
joint_names:
|
|
||||||
type: string
|
|
||||||
joint_positions:
|
|
||||||
type: string
|
|
||||||
move_group:
|
|
||||||
type: string
|
|
||||||
retry:
|
|
||||||
default: 10
|
|
||||||
type: string
|
|
||||||
speed:
|
|
||||||
default: 1
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- move_group
|
|
||||||
- joint_positions
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: moveit_joint_task参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-moveit_task:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
cartesian: false
|
|
||||||
move_group: null
|
|
||||||
offsets:
|
|
||||||
- 0
|
|
||||||
- 0
|
|
||||||
- 0
|
|
||||||
position: null
|
|
||||||
quaternion: null
|
|
||||||
retry: 10
|
|
||||||
speed: 1
|
|
||||||
target_link: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: moveit_task的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
cartesian:
|
|
||||||
default: false
|
|
||||||
type: string
|
|
||||||
move_group:
|
|
||||||
type: string
|
|
||||||
offsets:
|
|
||||||
default:
|
|
||||||
- 0
|
|
||||||
- 0
|
|
||||||
- 0
|
|
||||||
type: string
|
|
||||||
position:
|
|
||||||
type: string
|
|
||||||
quaternion:
|
|
||||||
type: string
|
|
||||||
retry:
|
|
||||||
default: 10
|
|
||||||
type: string
|
|
||||||
speed:
|
|
||||||
default: 1
|
|
||||||
type: string
|
|
||||||
target_link:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- move_group
|
|
||||||
- position
|
|
||||||
- quaternion
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: moveit_task参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-post_init:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
ros_node: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: post_init的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
ros_node:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- ros_node
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: post_init参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-resource_manager:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
parent_link: null
|
|
||||||
resource: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: resource_manager的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
parent_link:
|
|
||||||
type: string
|
|
||||||
resource:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- resource
|
|
||||||
- parent_link
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: resource_manager参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-wait_for_resource_action:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default: {}
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: wait_for_resource_action的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: wait_for_resource_action参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
pick_and_place:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
command: command
|
|
||||||
goal_default:
|
|
||||||
command: ''
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: ''
|
|
||||||
properties:
|
|
||||||
feedback:
|
|
||||||
properties:
|
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- status
|
|
||||||
title: SendCmd_Feedback
|
|
||||||
type: object
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
command:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- command
|
|
||||||
title: SendCmd_Goal
|
|
||||||
type: object
|
|
||||||
result:
|
|
||||||
properties:
|
|
||||||
return_info:
|
|
||||||
type: string
|
|
||||||
success:
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- return_info
|
|
||||||
- success
|
|
||||||
title: SendCmd_Result
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: SendCmd
|
|
||||||
type: object
|
|
||||||
type: SendCmd
|
|
||||||
set_position:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
command: command
|
|
||||||
goal_default:
|
|
||||||
command: ''
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: ''
|
|
||||||
properties:
|
|
||||||
feedback:
|
|
||||||
properties:
|
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- status
|
|
||||||
title: SendCmd_Feedback
|
|
||||||
type: object
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
command:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- command
|
|
||||||
title: SendCmd_Goal
|
|
||||||
type: object
|
|
||||||
result:
|
|
||||||
properties:
|
|
||||||
return_info:
|
|
||||||
type: string
|
|
||||||
success:
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- return_info
|
|
||||||
- success
|
|
||||||
title: SendCmd_Result
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: SendCmd
|
|
||||||
type: object
|
|
||||||
type: SendCmd
|
|
||||||
set_status:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
command: command
|
|
||||||
goal_default:
|
|
||||||
command: ''
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: ''
|
|
||||||
properties:
|
|
||||||
feedback:
|
|
||||||
properties:
|
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- status
|
|
||||||
title: SendCmd_Feedback
|
|
||||||
type: object
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
command:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- command
|
|
||||||
title: SendCmd_Goal
|
|
||||||
type: object
|
|
||||||
result:
|
|
||||||
properties:
|
|
||||||
return_info:
|
|
||||||
type: string
|
|
||||||
success:
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- return_info
|
|
||||||
- success
|
|
||||||
title: SendCmd_Result
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: SendCmd
|
|
||||||
type: object
|
|
||||||
type: SendCmd
|
|
||||||
module: unilabos.devices.ros_dev.moveit_interface:MoveitInterface
|
|
||||||
status_types: {}
|
|
||||||
type: python
|
|
||||||
config_info: []
|
|
||||||
description: 东洋XYZ三轴运动平台,基于MoveIt2运动规划框架的精密定位设备。该设备通过ROS2和MoveIt2实现三维空间的精确运动控制,支持复杂轨迹规划、多点定位、速度控制等功能。具备高精度定位、平稳运动、实时轨迹监控等特性。适用于精密加工、样品定位、检测扫描、自动化装配等需要高精度三维运动控制的实验室和工业应用场景。
|
|
||||||
handles: []
|
|
||||||
icon: ''
|
|
||||||
init_param_schema:
|
|
||||||
config:
|
|
||||||
properties:
|
|
||||||
device_config:
|
|
||||||
type: string
|
|
||||||
joint_poses:
|
|
||||||
type: string
|
|
||||||
moveit_type:
|
|
||||||
type: string
|
|
||||||
rotation:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- moveit_type
|
|
||||||
- joint_poses
|
|
||||||
type: object
|
|
||||||
data:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
model:
|
|
||||||
mesh: toyo_xyz
|
|
||||||
type: device
|
|
||||||
version: 1.0.0
|
|
||||||
@@ -1,3 +1,355 @@
|
|||||||
|
robotic_arm.SCARA_with_slider.virtual:
|
||||||
|
category:
|
||||||
|
- robot_arm
|
||||||
|
class:
|
||||||
|
action_value_mappings:
|
||||||
|
auto-check_tf_update_actions:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default: {}
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: check_tf_update_actions的参数schema
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties: {}
|
||||||
|
required: []
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: check_tf_update_actions参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-moveit_joint_task:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
joint_names: null
|
||||||
|
joint_positions: null
|
||||||
|
move_group: null
|
||||||
|
retry: 10
|
||||||
|
speed: 1
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: moveit_joint_task的参数schema
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
joint_names:
|
||||||
|
type: string
|
||||||
|
joint_positions:
|
||||||
|
type: string
|
||||||
|
move_group:
|
||||||
|
type: string
|
||||||
|
retry:
|
||||||
|
default: 10
|
||||||
|
type: string
|
||||||
|
speed:
|
||||||
|
default: 1
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- move_group
|
||||||
|
- joint_positions
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: moveit_joint_task参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-moveit_task:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
cartesian: false
|
||||||
|
move_group: null
|
||||||
|
offsets:
|
||||||
|
- 0
|
||||||
|
- 0
|
||||||
|
- 0
|
||||||
|
position: null
|
||||||
|
quaternion: null
|
||||||
|
retry: 10
|
||||||
|
speed: 1
|
||||||
|
target_link: null
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: moveit_task的参数schema
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
cartesian:
|
||||||
|
default: false
|
||||||
|
type: string
|
||||||
|
move_group:
|
||||||
|
type: string
|
||||||
|
offsets:
|
||||||
|
default:
|
||||||
|
- 0
|
||||||
|
- 0
|
||||||
|
- 0
|
||||||
|
type: string
|
||||||
|
position:
|
||||||
|
type: string
|
||||||
|
quaternion:
|
||||||
|
type: string
|
||||||
|
retry:
|
||||||
|
default: 10
|
||||||
|
type: string
|
||||||
|
speed:
|
||||||
|
default: 1
|
||||||
|
type: string
|
||||||
|
target_link:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- move_group
|
||||||
|
- position
|
||||||
|
- quaternion
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: moveit_task参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-post_init:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
ros_node: null
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: post_init的参数schema
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
ros_node:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- ros_node
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: post_init参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-resource_manager:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
parent_link: null
|
||||||
|
resource: null
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: resource_manager的参数schema
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
parent_link:
|
||||||
|
type: string
|
||||||
|
resource:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- resource
|
||||||
|
- parent_link
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: resource_manager参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-wait_for_resource_action:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default: {}
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: wait_for_resource_action的参数schema
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties: {}
|
||||||
|
required: []
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: wait_for_resource_action参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
pick_and_place:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
command: command
|
||||||
|
goal_default:
|
||||||
|
command: ''
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback:
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
title: SendCmd_Feedback
|
||||||
|
type: object
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
command:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- command
|
||||||
|
title: SendCmd_Goal
|
||||||
|
type: object
|
||||||
|
result:
|
||||||
|
properties:
|
||||||
|
return_info:
|
||||||
|
type: string
|
||||||
|
success:
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- return_info
|
||||||
|
- success
|
||||||
|
title: SendCmd_Result
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: SendCmd
|
||||||
|
type: object
|
||||||
|
type: SendCmd
|
||||||
|
set_position:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
command: command
|
||||||
|
goal_default:
|
||||||
|
command: ''
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback:
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
title: SendCmd_Feedback
|
||||||
|
type: object
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
command:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- command
|
||||||
|
title: SendCmd_Goal
|
||||||
|
type: object
|
||||||
|
result:
|
||||||
|
properties:
|
||||||
|
return_info:
|
||||||
|
type: string
|
||||||
|
success:
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- return_info
|
||||||
|
- success
|
||||||
|
title: SendCmd_Result
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: SendCmd
|
||||||
|
type: object
|
||||||
|
type: SendCmd
|
||||||
|
set_status:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
command: command
|
||||||
|
goal_default:
|
||||||
|
command: ''
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback:
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
title: SendCmd_Feedback
|
||||||
|
type: object
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
command:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- command
|
||||||
|
title: SendCmd_Goal
|
||||||
|
type: object
|
||||||
|
result:
|
||||||
|
properties:
|
||||||
|
return_info:
|
||||||
|
type: string
|
||||||
|
success:
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- return_info
|
||||||
|
- success
|
||||||
|
title: SendCmd_Result
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: SendCmd
|
||||||
|
type: object
|
||||||
|
type: SendCmd
|
||||||
|
module: unilabos.devices.ros_dev.moveit_interface:MoveitInterface
|
||||||
|
status_types: {}
|
||||||
|
type: python
|
||||||
|
config_info: []
|
||||||
|
description: 机械臂与滑块运动系统,基于MoveIt2运动规划框架的多自由度机械臂控制设备。该系统集成机械臂和线性滑块,通过ROS2和MoveIt2实现精确的轨迹规划和协调运动控制。支持笛卡尔空间和关节空间的运动规划、碰撞检测、逆运动学求解等功能。适用于复杂的pick-and-place操作、精密装配、多工位协作等需要高精度多轴协调运动的实验室自动化应用。
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema:
|
||||||
|
config:
|
||||||
|
properties:
|
||||||
|
device_config:
|
||||||
|
type: string
|
||||||
|
joint_poses:
|
||||||
|
type: string
|
||||||
|
moveit_type:
|
||||||
|
type: string
|
||||||
|
rotation:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- moveit_type
|
||||||
|
- joint_poses
|
||||||
|
type: object
|
||||||
|
data:
|
||||||
|
properties: {}
|
||||||
|
required: []
|
||||||
|
type: object
|
||||||
|
model:
|
||||||
|
mesh: arm_slider
|
||||||
|
type: device
|
||||||
|
version: 1.0.0
|
||||||
robotic_arm.UR:
|
robotic_arm.UR:
|
||||||
category:
|
category:
|
||||||
- robot_arm
|
- robot_arm
|
||||||
|
|||||||
@@ -470,6 +470,358 @@ linear_motion.grbl:
|
|||||||
- spindle_speed
|
- spindle_speed
|
||||||
type: object
|
type: object
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
linear_motion.toyo_xyz.sim:
|
||||||
|
category:
|
||||||
|
- robot_linear_motion
|
||||||
|
class:
|
||||||
|
action_value_mappings:
|
||||||
|
auto-check_tf_update_actions:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default: {}
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: check_tf_update_actions的参数schema
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties: {}
|
||||||
|
required: []
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: check_tf_update_actions参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-moveit_joint_task:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
joint_names: null
|
||||||
|
joint_positions: null
|
||||||
|
move_group: null
|
||||||
|
retry: 10
|
||||||
|
speed: 1
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: moveit_joint_task的参数schema
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
joint_names:
|
||||||
|
type: string
|
||||||
|
joint_positions:
|
||||||
|
type: string
|
||||||
|
move_group:
|
||||||
|
type: string
|
||||||
|
retry:
|
||||||
|
default: 10
|
||||||
|
type: string
|
||||||
|
speed:
|
||||||
|
default: 1
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- move_group
|
||||||
|
- joint_positions
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: moveit_joint_task参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-moveit_task:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
cartesian: false
|
||||||
|
move_group: null
|
||||||
|
offsets:
|
||||||
|
- 0
|
||||||
|
- 0
|
||||||
|
- 0
|
||||||
|
position: null
|
||||||
|
quaternion: null
|
||||||
|
retry: 10
|
||||||
|
speed: 1
|
||||||
|
target_link: null
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: moveit_task的参数schema
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
cartesian:
|
||||||
|
default: false
|
||||||
|
type: string
|
||||||
|
move_group:
|
||||||
|
type: string
|
||||||
|
offsets:
|
||||||
|
default:
|
||||||
|
- 0
|
||||||
|
- 0
|
||||||
|
- 0
|
||||||
|
type: string
|
||||||
|
position:
|
||||||
|
type: string
|
||||||
|
quaternion:
|
||||||
|
type: string
|
||||||
|
retry:
|
||||||
|
default: 10
|
||||||
|
type: string
|
||||||
|
speed:
|
||||||
|
default: 1
|
||||||
|
type: string
|
||||||
|
target_link:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- move_group
|
||||||
|
- position
|
||||||
|
- quaternion
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: moveit_task参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-post_init:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
ros_node: null
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: post_init的参数schema
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
ros_node:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- ros_node
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: post_init参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-resource_manager:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default:
|
||||||
|
parent_link: null
|
||||||
|
resource: null
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: resource_manager的参数schema
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
parent_link:
|
||||||
|
type: string
|
||||||
|
resource:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- resource
|
||||||
|
- parent_link
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: resource_manager参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
auto-wait_for_resource_action:
|
||||||
|
feedback: {}
|
||||||
|
goal: {}
|
||||||
|
goal_default: {}
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: wait_for_resource_action的参数schema
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties: {}
|
||||||
|
required: []
|
||||||
|
type: object
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: wait_for_resource_action参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
pick_and_place:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
command: command
|
||||||
|
goal_default:
|
||||||
|
command: ''
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback:
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
title: SendCmd_Feedback
|
||||||
|
type: object
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
command:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- command
|
||||||
|
title: SendCmd_Goal
|
||||||
|
type: object
|
||||||
|
result:
|
||||||
|
properties:
|
||||||
|
return_info:
|
||||||
|
type: string
|
||||||
|
success:
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- return_info
|
||||||
|
- success
|
||||||
|
title: SendCmd_Result
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: SendCmd
|
||||||
|
type: object
|
||||||
|
type: SendCmd
|
||||||
|
set_position:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
command: command
|
||||||
|
goal_default:
|
||||||
|
command: ''
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback:
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
title: SendCmd_Feedback
|
||||||
|
type: object
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
command:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- command
|
||||||
|
title: SendCmd_Goal
|
||||||
|
type: object
|
||||||
|
result:
|
||||||
|
properties:
|
||||||
|
return_info:
|
||||||
|
type: string
|
||||||
|
success:
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- return_info
|
||||||
|
- success
|
||||||
|
title: SendCmd_Result
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: SendCmd
|
||||||
|
type: object
|
||||||
|
type: SendCmd
|
||||||
|
set_status:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
command: command
|
||||||
|
goal_default:
|
||||||
|
command: ''
|
||||||
|
handles: []
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: ''
|
||||||
|
properties:
|
||||||
|
feedback:
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
title: SendCmd_Feedback
|
||||||
|
type: object
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
command:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- command
|
||||||
|
title: SendCmd_Goal
|
||||||
|
type: object
|
||||||
|
result:
|
||||||
|
properties:
|
||||||
|
return_info:
|
||||||
|
type: string
|
||||||
|
success:
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- return_info
|
||||||
|
- success
|
||||||
|
title: SendCmd_Result
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: SendCmd
|
||||||
|
type: object
|
||||||
|
type: SendCmd
|
||||||
|
module: unilabos.devices.ros_dev.moveit_interface:MoveitInterface
|
||||||
|
status_types: {}
|
||||||
|
type: python
|
||||||
|
config_info: []
|
||||||
|
description: 东洋XYZ三轴运动平台,基于MoveIt2运动规划框架的精密定位设备。该设备通过ROS2和MoveIt2实现三维空间的精确运动控制,支持复杂轨迹规划、多点定位、速度控制等功能。具备高精度定位、平稳运动、实时轨迹监控等特性。适用于精密加工、样品定位、检测扫描、自动化装配等需要高精度三维运动控制的实验室和工业应用场景。
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema:
|
||||||
|
config:
|
||||||
|
properties:
|
||||||
|
device_config:
|
||||||
|
type: string
|
||||||
|
joint_poses:
|
||||||
|
type: string
|
||||||
|
moveit_type:
|
||||||
|
type: string
|
||||||
|
rotation:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- moveit_type
|
||||||
|
- joint_poses
|
||||||
|
type: object
|
||||||
|
data:
|
||||||
|
properties: {}
|
||||||
|
required: []
|
||||||
|
type: object
|
||||||
|
model:
|
||||||
|
mesh: toyo_xyz
|
||||||
|
type: device
|
||||||
|
version: 1.0.0
|
||||||
motor.iCL42:
|
motor.iCL42:
|
||||||
category:
|
category:
|
||||||
- robot_linear_motion
|
- robot_linear_motion
|
||||||
|
|||||||
@@ -1,315 +0,0 @@
|
|||||||
lh_joint_publisher:
|
|
||||||
category:
|
|
||||||
- sim_nodes
|
|
||||||
class:
|
|
||||||
action_value_mappings:
|
|
||||||
auto-check_tf_update_actions:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default: {}
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: check_tf_update_actions的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: check_tf_update_actions参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-find_resource_parent:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
resource_id: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: find_resource_parent的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
resource_id:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- resource_id
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: find_resource_parent参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-inverse_kinematics:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
parent_id: null
|
|
||||||
x: null
|
|
||||||
x_joint: null
|
|
||||||
y: null
|
|
||||||
y_joint: null
|
|
||||||
z: null
|
|
||||||
z_joint: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: inverse_kinematics的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
parent_id:
|
|
||||||
type: string
|
|
||||||
x:
|
|
||||||
type: string
|
|
||||||
x_joint:
|
|
||||||
type: object
|
|
||||||
y:
|
|
||||||
type: string
|
|
||||||
y_joint:
|
|
||||||
type: object
|
|
||||||
z:
|
|
||||||
type: string
|
|
||||||
z_joint:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- x
|
|
||||||
- y
|
|
||||||
- z
|
|
||||||
- parent_id
|
|
||||||
- x_joint
|
|
||||||
- y_joint
|
|
||||||
- z_joint
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: inverse_kinematics参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-lh_joint_action_callback:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
goal_handle: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: lh_joint_action_callback的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
goal_handle:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- goal_handle
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: lh_joint_action_callback参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-lh_joint_pub_callback:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default: {}
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: lh_joint_pub_callback的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: lh_joint_pub_callback参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-move_joints:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
option: null
|
|
||||||
resource_names: null
|
|
||||||
speed: 0.1
|
|
||||||
x: null
|
|
||||||
x_joint: null
|
|
||||||
y: null
|
|
||||||
y_joint: null
|
|
||||||
z: null
|
|
||||||
z_joint: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: move_joints的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
option:
|
|
||||||
type: string
|
|
||||||
resource_names:
|
|
||||||
type: string
|
|
||||||
speed:
|
|
||||||
default: 0.1
|
|
||||||
type: string
|
|
||||||
x:
|
|
||||||
type: string
|
|
||||||
x_joint:
|
|
||||||
type: string
|
|
||||||
y:
|
|
||||||
type: string
|
|
||||||
y_joint:
|
|
||||||
type: string
|
|
||||||
z:
|
|
||||||
type: string
|
|
||||||
z_joint:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- resource_names
|
|
||||||
- x
|
|
||||||
- y
|
|
||||||
- z
|
|
||||||
- option
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: move_joints参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-move_to:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
joint_positions: null
|
|
||||||
parent_id: null
|
|
||||||
speed: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: move_to的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
joint_positions:
|
|
||||||
type: string
|
|
||||||
parent_id:
|
|
||||||
type: string
|
|
||||||
speed:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- joint_positions
|
|
||||||
- speed
|
|
||||||
- parent_id
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: move_to参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-resource_move:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
channels: null
|
|
||||||
link_name: null
|
|
||||||
resource_id: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: resource_move的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
channels:
|
|
||||||
type: array
|
|
||||||
link_name:
|
|
||||||
type: string
|
|
||||||
resource_id:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- resource_id
|
|
||||||
- link_name
|
|
||||||
- channels
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: resource_move参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-send_resource_action:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
link_name: null
|
|
||||||
resource_id_list: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: send_resource_action的参数schema
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
link_name:
|
|
||||||
type: string
|
|
||||||
resource_id_list:
|
|
||||||
type: array
|
|
||||||
required:
|
|
||||||
- resource_id_list
|
|
||||||
- link_name
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: send_resource_action参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
module: unilabos.devices.ros_dev.liquid_handler_joint_publisher:LiquidHandlerJointPublisher
|
|
||||||
status_types: {}
|
|
||||||
type: ros2
|
|
||||||
config_info: []
|
|
||||||
description: 液体处理器关节发布器,用于ROS2仿真系统中的液体处理设备运动控制。该节点通过发布关节状态驱动仿真模型中的机械臂运动,支持三维坐标到关节空间的逆运动学转换、多关节协调控制、资源跟踪和TF变换。具备精确的位置控制、速度调节、pick-and-place操作等功能。适用于液体处理系统的虚拟仿真、运动规划验证、系统集成测试等应用场景。
|
|
||||||
handles: []
|
|
||||||
icon: ''
|
|
||||||
init_param_schema:
|
|
||||||
config:
|
|
||||||
properties:
|
|
||||||
device_id:
|
|
||||||
default: lh_joint_publisher
|
|
||||||
type: string
|
|
||||||
rate:
|
|
||||||
default: 50
|
|
||||||
type: string
|
|
||||||
resource_tracker:
|
|
||||||
type: string
|
|
||||||
resources_config:
|
|
||||||
type: array
|
|
||||||
required:
|
|
||||||
- resources_config
|
|
||||||
- resource_tracker
|
|
||||||
type: object
|
|
||||||
data:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
version: 1.0.0
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
laiyu_add_solid:
|
solid_dispenser.laiyu:
|
||||||
category:
|
category:
|
||||||
- laiyu_add_solid
|
- solid_dispenser
|
||||||
class:
|
class:
|
||||||
action_value_mappings:
|
action_value_mappings:
|
||||||
add_powder_tube:
|
add_powder_tube:
|
||||||
@@ -2239,6 +2239,14 @@ virtual_multiway_valve:
|
|||||||
io_type: source
|
io_type: source
|
||||||
label: '7'
|
label: '7'
|
||||||
side: WEST
|
side: WEST
|
||||||
|
- data_key: fluid_port_8
|
||||||
|
data_source: executor
|
||||||
|
data_type: fluid
|
||||||
|
description: 八通阀门端口8-特殊输入
|
||||||
|
handler_key: '8'
|
||||||
|
io_type: target
|
||||||
|
label: '8'
|
||||||
|
side: WEST
|
||||||
- data_key: fluid_port_8
|
- data_key: fluid_port_8
|
||||||
data_source: executor
|
data_source: executor
|
||||||
data_type: fluid
|
data_type: fluid
|
||||||
@@ -3983,14 +3991,6 @@ virtual_separator:
|
|||||||
io_type: source
|
io_type: source
|
||||||
label: bottom_phase_out
|
label: bottom_phase_out
|
||||||
side: SOUTH
|
side: SOUTH
|
||||||
- data_key: top_outlet
|
|
||||||
data_source: executor
|
|
||||||
data_type: fluid
|
|
||||||
description: 上相(轻相)液体输出口
|
|
||||||
handler_key: topphaseout
|
|
||||||
io_type: source
|
|
||||||
label: top_phase_out
|
|
||||||
side: EAST
|
|
||||||
- data_key: mechanical_port
|
- data_key: mechanical_port
|
||||||
data_source: handle
|
data_source: handle
|
||||||
data_type: mechanical
|
data_type: mechanical
|
||||||
|
|||||||
@@ -1262,6 +1262,11 @@ workstation:
|
|||||||
data_type: resource
|
data_type: resource
|
||||||
handler_key: solvent
|
handler_key: solvent
|
||||||
label: Solvent
|
label: Solvent
|
||||||
|
- data_key: reagent
|
||||||
|
data_source: handle
|
||||||
|
data_type: resource
|
||||||
|
handler_key: reagent
|
||||||
|
label: Reagent
|
||||||
output:
|
output:
|
||||||
- data_key: vessel
|
- data_key: vessel
|
||||||
data_source: executor
|
data_source: executor
|
||||||
|
|||||||
@@ -1,183 +0,0 @@
|
|||||||
zhida_hplc:
|
|
||||||
category:
|
|
||||||
- zhida_hplc
|
|
||||||
class:
|
|
||||||
action_value_mappings:
|
|
||||||
abort:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default: {}
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: ''
|
|
||||||
properties:
|
|
||||||
feedback:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
title: EmptyIn_Feedback
|
|
||||||
type: object
|
|
||||||
goal:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
title: EmptyIn_Goal
|
|
||||||
type: object
|
|
||||||
result:
|
|
||||||
properties:
|
|
||||||
return_info:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- return_info
|
|
||||||
title: EmptyIn_Result
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: EmptyIn
|
|
||||||
type: object
|
|
||||||
type: EmptyIn
|
|
||||||
auto-close:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default: {}
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: HPLC设备连接关闭函数。安全地断开与智达HPLC设备的TCP socket连接,释放网络资源。该函数确保连接的正确关闭,避免网络资源泄露。通常在设备使用完毕或系统关闭时调用。
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: close参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-connect:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default: {}
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: HPLC设备连接建立函数。与智达HPLC设备建立TCP socket通信连接,配置通信超时参数。该函数是设备使用前的必要步骤,建立成功后可进行状态查询、方法获取、任务启动等操作。连接失败时会抛出异常。
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: connect参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
get_methods:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default: {}
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: ''
|
|
||||||
properties:
|
|
||||||
feedback:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
title: EmptyIn_Feedback
|
|
||||||
type: object
|
|
||||||
goal:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
title: EmptyIn_Goal
|
|
||||||
type: object
|
|
||||||
result:
|
|
||||||
properties:
|
|
||||||
return_info:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- return_info
|
|
||||||
title: EmptyIn_Result
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: EmptyIn
|
|
||||||
type: object
|
|
||||||
type: EmptyIn
|
|
||||||
start:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
string: string
|
|
||||||
goal_default:
|
|
||||||
string: ''
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: ''
|
|
||||||
properties:
|
|
||||||
feedback:
|
|
||||||
properties: {}
|
|
||||||
required: []
|
|
||||||
title: StrSingleInput_Feedback
|
|
||||||
type: object
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
string:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- string
|
|
||||||
title: StrSingleInput_Goal
|
|
||||||
type: object
|
|
||||||
result:
|
|
||||||
properties:
|
|
||||||
return_info:
|
|
||||||
type: string
|
|
||||||
success:
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- return_info
|
|
||||||
- success
|
|
||||||
title: StrSingleInput_Result
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: StrSingleInput
|
|
||||||
type: object
|
|
||||||
type: StrSingleInput
|
|
||||||
module: unilabos.devices.zhida_hplc.zhida:ZhidaClient
|
|
||||||
status_types:
|
|
||||||
methods: dict
|
|
||||||
status: dict
|
|
||||||
type: python
|
|
||||||
config_info: []
|
|
||||||
description: 智达高效液相色谱(HPLC)分析设备,用于实验室样品的分离、检测和定量分析。该设备通过TCP socket与HPLC控制系统通信,支持远程控制和状态监控。具备自动进样、梯度洗脱、多检测器数据采集等功能,可执行复杂的色谱分析方法。适用于化学分析、药物检测、环境监测、生物样品分析等需要高精度分离分析的实验室应用场景。
|
|
||||||
handles: []
|
|
||||||
icon: ''
|
|
||||||
init_param_schema:
|
|
||||||
config:
|
|
||||||
properties:
|
|
||||||
host:
|
|
||||||
default: 192.168.1.47
|
|
||||||
type: string
|
|
||||||
port:
|
|
||||||
default: 5792
|
|
||||||
type: string
|
|
||||||
timeout:
|
|
||||||
default: 10.0
|
|
||||||
type: string
|
|
||||||
required: []
|
|
||||||
type: object
|
|
||||||
data:
|
|
||||||
properties:
|
|
||||||
methods:
|
|
||||||
type: object
|
|
||||||
status:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- status
|
|
||||||
- methods
|
|
||||||
type: object
|
|
||||||
version: 1.0.0
|
|
||||||
@@ -28,7 +28,7 @@ container:
|
|||||||
handler_key: bind
|
handler_key: bind
|
||||||
io_type: target
|
io_type: target
|
||||||
label: bind
|
label: bind
|
||||||
side: SOUTH
|
side: WEST
|
||||||
icon: Flask.webp
|
icon: Flask.webp
|
||||||
init_param_schema: {}
|
init_param_schema: {}
|
||||||
registry_type: resource
|
registry_type: resource
|
||||||
|
|||||||
Reference in New Issue
Block a user