Compare commits

...

11 Commits

Author SHA1 Message Date
Xuwznln
ac88c59b50 protocol node不再嵌套显示 2025-06-17 16:10:28 +08:00
Xuwznln
2492af57c0 完善tip 2025-06-17 15:59:32 +08:00
Xuwznln
b1dae6da17 默认不进行注册表报送,通过命令unilabos-register或者增加启动参数 2025-06-17 15:58:56 +08:00
Junhan Chang
af812d630a separate registry sync and resource_add 2025-06-17 15:37:36 +08:00
Xuwznln
183579fd7f 移除device的父节点关联 2025-06-17 15:34:03 +08:00
Junhan Chang
678ace6109 Fix edge id 2025-06-17 13:53:38 +08:00
Junhan Chang
18c4eb3e4d fix device ports 2025-06-17 13:27:27 +08:00
Junhan Chang
dd7abe987e fix resource and edge upload 2025-06-17 13:27:01 +08:00
KCFeng425
3e6c8d6340 修改了json图中link的格式 2025-06-17 13:01:55 +08:00
KCFeng425
ab7f1539af Merge branch 'device-registry-port' of github.com:KCFeng425/Uni-Lab-OS into device-registry-port 2025-06-17 10:00:19 +08:00
KCFeng425
ee918a0e93 添加了icon的文件名在注册表里面 2025-06-17 10:00:11 +08:00
15 changed files with 368 additions and 246 deletions

View File

@@ -17,6 +17,7 @@ setup(
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [
"unilab = unilabos.app.main:main", "unilab = unilabos.app.main:main",
"unilab-register = unilabos.app.register:main"
], ],
}, },
) )

View File

@@ -6,8 +6,8 @@
virtual_heatchill() 加热器 virtual_heatchill() 加热器
virtual_stirrer() 搅拌器 virtual_stirrer() 搅拌器
virtual_solenoid_valve() 电磁阀 virtual_solenoid_valve() 电磁阀
vacuum_pump() vacuum_pump.mock 真空泵 virtual_vacuum_pump() vacuum_pump.mock 真空泵
gas_source() 气源 virtual_gas_source() 气源
virtual_filter() 过滤器 virtual_filter() 过滤器
virtual_column(√) 层析柱 virtual_column(√) 层析柱
separator() homemade_grbl_conductivity 分液漏斗 separator() homemade_grbl_conductivity 分液漏斗
@@ -24,9 +24,9 @@
DissolveProtocol() DissolveProtocol()
FilterThroughProtocol() FilterThroughProtocol()
WashSolidProtocol() WashSolidProtocol()
SeparateProtocol() SeparateProtocol()
EvaporateProtocol() EvaporateProtocol()
HeatChillProtocol() HeatChillProtocol()
HeatChillStartProtocol() HeatChillStartProtocol()
HeatChillStopProtocol() HeatChillStopProtocol()
EvacuateAndRefillProtocol() EvacuateAndRefillProtocol()

View File

@@ -59,7 +59,8 @@
"HeatChillProtocol", "HeatChillProtocol",
"HeatChillStartProtocol", "HeatChillStartProtocol",
"HeatChillStopProtocol", "HeatChillStopProtocol",
"EvacuateAndRefillProtocol" "EvacuateAndRefillProtocol",
"PumpTransferProtocol"
] ]
}, },
"data": {} "data": {}
@@ -152,7 +153,7 @@
"children": [], "children": [],
"parent": "ComprehensiveProtocolStation", "parent": "ComprehensiveProtocolStation",
"type": "container", "type": "container",
"class": null, "class": "container",
"position": { "position": {
"x": 200, "x": 200,
"y": 150, "y": 150,
@@ -173,7 +174,7 @@
"children": [], "children": [],
"parent": "ComprehensiveProtocolStation", "parent": "ComprehensiveProtocolStation",
"type": "container", "type": "container",
"class": null, "class": "container",
"position": { "position": {
"x": 250, "x": 250,
"y": 150, "y": 150,
@@ -194,7 +195,7 @@
"children": [], "children": [],
"parent": "ComprehensiveProtocolStation", "parent": "ComprehensiveProtocolStation",
"type": "container", "type": "container",
"class": null, "class": "container",
"position": { "position": {
"x": 300, "x": 300,
"y": 150, "y": 150,
@@ -215,7 +216,7 @@
"children": [], "children": [],
"parent": "ComprehensiveProtocolStation", "parent": "ComprehensiveProtocolStation",
"type": "container", "type": "container",
"class": null, "class": "container",
"position": { "position": {
"x": 900, "x": 900,
"y": 150, "y": 150,
@@ -236,7 +237,7 @@
"children": [], "children": [],
"parent": "ComprehensiveProtocolStation", "parent": "ComprehensiveProtocolStation",
"type": "container", "type": "container",
"class": null, "class": "container",
"position": { "position": {
"x": 950, "x": 950,
"y": 150, "y": 150,
@@ -301,7 +302,7 @@
"children": [], "children": [],
"parent": "ComprehensiveProtocolStation", "parent": "ComprehensiveProtocolStation",
"type": "container", "type": "container",
"class": null, "class": "container",
"position": { "position": {
"x": 400, "x": 400,
"y": 450, "y": 450,
@@ -386,7 +387,7 @@
"children": [], "children": [],
"parent": "ComprehensiveProtocolStation", "parent": "ComprehensiveProtocolStation",
"type": "container", "type": "container",
"class": null, "class": "container",
"position": { "position": {
"x": 500, "x": 500,
"y": 400, "y": 400,
@@ -405,7 +406,7 @@
"children": [], "children": [],
"parent": "ComprehensiveProtocolStation", "parent": "ComprehensiveProtocolStation",
"type": "container", "type": "container",
"class": null, "class": "container",
"position": { "position": {
"x": 1100, "x": 1100,
"y": 500, "y": 500,
@@ -569,7 +570,7 @@
"children": [], "children": [],
"parent": "ComprehensiveProtocolStation", "parent": "ComprehensiveProtocolStation",
"type": "container", "type": "container",
"class": null, "class": "container",
"position": { "position": {
"x": 900, "x": 900,
"y": 500, "y": 500,
@@ -588,7 +589,7 @@
"children": [], "children": [],
"parent": "ComprehensiveProtocolStation", "parent": "ComprehensiveProtocolStation",
"type": "container", "type": "container",
"class": null, "class": "container",
"position": { "position": {
"x": 950, "x": 950,
"y": 500, "y": 500,
@@ -607,7 +608,7 @@
"children": [], "children": [],
"parent": "ComprehensiveProtocolStation", "parent": "ComprehensiveProtocolStation",
"type": "container", "type": "container",
"class": null, "class": "container",
"position": { "position": {
"x": 1050, "x": 1050,
"y": 500, "y": 500,
@@ -626,257 +627,261 @@
"id": "link_valve1_pump1", "id": "link_valve1_pump1",
"source": "multiway_valve_1", "source": "multiway_valve_1",
"target": "transfer_pump_1", "target": "transfer_pump_1",
"source_port": "port_0",
"target_port": "inlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_1": "port_0" "multiway_valve_1": "transferpump",
"transfer_pump_1": "transferpump"
} }
}, },
{ {
"id": "link_valve1_reagent1", "id": "link_valve1_reagent1",
"source": "multiway_valve_1", "source": "multiway_valve_1",
"target": "reagent_bottle_1", "target": "reagent_bottle_1",
"source_port": "port_1",
"target_port": "outlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_1": "port_1" "multiway_valve_1": "1",
"reagent_bottle_1": "top"
} }
}, },
{ {
"id": "link_valve1_reagent2", "id": "link_valve1_reagent2",
"source": "multiway_valve_1", "source": "multiway_valve_1",
"target": "reagent_bottle_2", "target": "reagent_bottle_2",
"source_port": "port_2",
"target_port": "outlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_1": "port_2" "multiway_valve_1": "2",
"reagent_bottle_2": "top"
} }
}, },
{ {
"id": "link_valve1_reagent3", "id": "link_valve1_reagent3",
"source": "multiway_valve_1", "source": "multiway_valve_1",
"target": "reagent_bottle_3", "target": "reagent_bottle_3",
"source_port": "port_3",
"target_port": "outlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_1": "port_3" "multiway_valve_1": "3",
"reagent_bottle_3": "top"
} }
}, },
{ {
"id": "link_valve1_centrifuge", "id": "link_valve1_centrifuge",
"source": "multiway_valve_1", "source": "multiway_valve_1",
"target": "centrifuge_1", "target": "centrifuge_1",
"source_port": "port_4",
"target_port": "inlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_1": "port_4" "multiway_valve_1": "4",
"centrifuge_1": "centrifuge"
} }
}, },
{ {
"id": "link_valve1_rotavap", "id": "link_valve1_rotavap",
"source": "multiway_valve_1", "source": "multiway_valve_1",
"target": "rotavap_1", "target": "rotavap_1",
"source_port": "port_5",
"target_port": "inlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_1": "port_5" "multiway_valve_1": "5",
"rotavap_1": "rotavap-sample"
} }
}, },
{ {
"id": "link_valve1_reactor", "id": "link_valve1_reactor",
"source": "multiway_valve_1", "source": "multiway_valve_1",
"target": "main_reactor", "target": "main_reactor",
"source_port": "port_6",
"target_port": "inlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_1": "port_6" "multiway_valve_1": "6",
"main_reactor": "top"
} }
}, },
{ {
"id": "link_valve1_waste1", "id": "link_valve1_waste1",
"source": "multiway_valve_1", "source": "multiway_valve_1",
"target": "waste_bottle_1", "target": "waste_bottle_1",
"source_port": "port_7",
"target_port": "inlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_1": "port_7" "multiway_valve_1": "7",
"waste_bottle_1": "top"
} }
}, },
{ {
"id": "link_valve1_valve2", "id": "link_valve1_valve2",
"source": "multiway_valve_1", "source": "multiway_valve_1",
"target": "multiway_valve_2", "target": "multiway_valve_2",
"source_port": "port_8",
"target_port": "port_1",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_1": "port_8", "multiway_valve_1": "8",
"multiway_valve_2": "port_1" "multiway_valve_2": "1"
} }
}, },
{ {
"id": "link_valve2_pump2", "id": "link_valve2_pump2",
"source": "multiway_valve_2", "source": "multiway_valve_2",
"target": "transfer_pump_2", "target": "transfer_pump_2",
"source_port": "port_0",
"target_port": "inlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_2": "port_0" "multiway_valve_2": "transferpump",
"transfer_pump_2": "transferpump"
} }
}, },
{ {
"id": "link_valve2_solenoid1", "id": "link_valve2_solenoid1",
"source": "multiway_valve_2", "source": "multiway_valve_2",
"target": "solenoid_valve_1", "target": "solenoid_valve_1",
"source_port": "port_2",
"target_port": "inlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_2": "port_2" "multiway_valve_2": "2",
"solenoid_valve_1": "in"
} }
}, },
{ {
"id": "link_solenoid1_vacuum", "id": "link_solenoid1_vacuum",
"source": "solenoid_valve_1", "source": "solenoid_valve_1",
"target": "vacuum_pump_1", "target": "vacuum_pump_1",
"source_port": "outlet", "type": "fluid",
"target_port": "inlet", "port": {
"type": "fluid" "solenoid_valve_1": "out",
"vacuum_pump_1": "vacuumpump"
}
}, },
{ {
"id": "link_valve2_solenoid2", "id": "link_valve2_solenoid2",
"source": "multiway_valve_2", "source": "multiway_valve_2",
"target": "solenoid_valve_2", "target": "solenoid_valve_2",
"source_port": "port_3",
"target_port": "inlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_2": "port_3" "multiway_valve_2": "3",
"solenoid_valve_2": "in"
} }
}, },
{ {
"id": "link_solenoid2_gas", "id": "link_solenoid2_gas",
"source": "solenoid_valve_2", "source": "solenoid_valve_2",
"target": "gas_source_1", "target": "gas_source_1",
"source_port": "outlet", "type": "fluid",
"target_port": "outlet", "port": {
"type": "fluid" "solenoid_valve_2": "out",
"gas_source_1": "gassource"
}
}, },
{ {
"id": "link_valve2_filter", "id": "link_valve2_filter",
"source": "multiway_valve_2", "source": "multiway_valve_2",
"target": "filter_1", "target": "filter_1",
"source_port": "port_4",
"target_port": "inlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_2": "port_4" "multiway_valve_2": "4",
"filter_1": "filterin"
} }
}, },
{ {
"id": "link_filter_collection1", "id": "link_filter_collection1",
"source": "filter_1", "source": "filter_1",
"target": "collection_bottle_1", "target": "collection_bottle_1",
"source_port": "filtrate_outlet", "type": "fluid",
"target_port": "inlet", "port": {
"type": "fluid" "filter_1": "filtrate_out",
"collection_bottle_1": "top"
}
}, },
{ {
"id": "link_valve2_column", "id": "link_valve2_column",
"source": "multiway_valve_2", "source": "multiway_valve_2",
"target": "column_1", "target": "column_1",
"source_port": "port_5",
"target_port": "inlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_2": "port_5" "multiway_valve_2": "5",
"column_1": "columnin"
} }
}, },
{ {
"id": "link_column_collection2", "id": "link_column_collection2",
"source": "column_1", "source": "column_1",
"target": "collection_bottle_2", "target": "collection_bottle_2",
"source_port": "outlet", "type": "fluid",
"target_port": "inlet", "port": {
"type": "fluid" "column_1": "columnout",
"collection_bottle_2": "top"
}
}, },
{ {
"id": "link_valve2_separator", "id": "link_valve2_separator",
"source": "multiway_valve_2", "source": "multiway_valve_2",
"target": "separator_1", "target": "separator_1",
"source_port": "port_6",
"target_port": "inlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_2": "port_6" "multiway_valve_2": "6",
"separator_1": "separatorin"
} }
}, },
{ {
"id": "link_separator_collection3", "id": "link_separator_collection3",
"source": "separator_1", "source": "separator_1",
"target": "collection_bottle_3", "target": "collection_bottle_3",
"source_port": "top_outlet", "type": "fluid",
"target_port": "inlet", "port": {
"type": "fluid" "separator_1": "separatorout",
"collection_bottle_3": "top"
}
}, },
{ {
"id": "link_separator_stirrer_2", "id": "link_separator_stirrer_2",
"source": "separator_1", "source": "separator_1",
"target": "stirrer_2", "target": "stirrer_2",
"source_port": "top_outlet", "type": "fluid",
"target_port": "inlet", "port": {
"type": "fluid" "separator_1": "separatorout",
"stirrer_2": "stirrer"
}
}, },
{ {
"id": "link_separator_waste2", "id": "link_separator_waste2",
"source": "separator_1", "source": "separator_1",
"target": "waste_bottle_2", "target": "waste_bottle_2",
"source_port": "bottom_outlet", "type": "fluid",
"target_port": "inlet", "port": {
"type": "fluid" "separator_1": "separatorout",
"waste_bottle_2": "top"
}
}, },
{ {
"id": "link_valve2_reagent4", "id": "link_valve2_reagent4",
"source": "multiway_valve_2", "source": "multiway_valve_2",
"target": "reagent_bottle_4", "target": "reagent_bottle_4",
"source_port": "port_7",
"target_port": "outlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_2": "port_7" "multiway_valve_2": "7",
"reagent_bottle_4": "top"
} }
}, },
{ {
"id": "link_valve2_reagent5", "id": "link_valve2_reagent5",
"source": "multiway_valve_2", "source": "multiway_valve_2",
"target": "reagent_bottle_5", "target": "reagent_bottle_5",
"source_port": "port_8",
"target_port": "outlet",
"type": "fluid", "type": "fluid",
"port": { "port": {
"multiway_valve_2": "port_8" "multiway_valve_2": "8",
"reagent_bottle_5": "top"
} }
}, },
{ {
"id": "mech_stirrer_reactor", "id": "mech_stirrer_reactor",
"source": "stirrer_1", "source": "stirrer_1",
"target": "main_reactor", "target": "main_reactor",
"type": "fluid" "type": "fluid",
"port": {
"stirrer_1": "stirrer",
"main_reactor": "top"
}
}, },
{ {
"id": "thermal_heater_reactor", "id": "thermal_heater_reactor",
"source": "heater_1", "source": "heater_1",
"target": "main_reactor", "target": "main_reactor",
"type": "fluid" "type": "fluid",
"port": {
"heater_1": "heatchill",
"main_reactor": "bottom"
}
} }
] ]
} }

View File

@@ -122,39 +122,39 @@
], ],
"links": [ "links": [
{ {
"source": "reactor", "source": "vacuum_valve",
"target": "vacuum_valve", "target": "reactor",
"type": "physical", "type": "fluid",
"port": { "port": {
"reactor": "top", "reactor": "top",
"vacuum_valve": "1" "vacuum_valve": "out"
} }
}, },
{ {
"source": "reactor", "source": "gas_valve",
"target": "gas_valve", "target": "reactor",
"type": "physical", "type": "fluid",
"port": { "port": {
"reactor": "top", "reactor": "top",
"gas_valve": "1" "gas_valve": "out"
} }
}, },
{ {
"source": "vacuum_pump", "source": "vacuum_valve",
"target": "vacuum_valve", "target": "vacuum_pump",
"type": "physical", "type": "fluid",
"port": { "port": {
"vacuum_pump": "out", "vacuum_pump": "out",
"vacuum_valve": "0" "vacuum_valve": "in"
} }
}, },
{ {
"source": "gas_source", "source": "gas_valve",
"target": "gas_valve", "target": "gas_source",
"type": "physical", "type": "fluid",
"port": { "port": {
"gas_source": "out", "gas_source": "out",
"gas_valve": "0" "gas_valve": "in"
} }
} }
] ]

View File

@@ -22,6 +22,21 @@ from unilabos.config.config import load_config, BasicConfig, _update_config_from
from unilabos.utils.banner_print import print_status, print_unilab_banner from unilabos.utils.banner_print import print_status, print_unilab_banner
def load_config_from_file(config_path):
if config_path is None:
config_path = os.environ.get("UNILABOS.BASICCONFIG.CONFIG_PATH", None)
if config_path:
if not os.path.exists(config_path):
print_status(f"配置文件 {config_path} 不存在", "error")
elif not config_path.endswith(".py"):
print_status(f"配置文件 {config_path} 不是Python文件必须以.py结尾", "error")
else:
load_config(config_path)
else:
print_status(f"启动 UniLab-OS时配置文件参数未正确传入 --config '{config_path}' 尝试本地配置...", "warning")
load_config(config_path)
def parse_args(): def parse_args():
"""解析命令行参数""" """解析命令行参数"""
parser = argparse.ArgumentParser(description="Start Uni-Lab Edge server.") parser = argparse.ArgumentParser(description="Start Uni-Lab Edge server.")
@@ -58,6 +73,11 @@ def parse_args():
action="store_true", action="store_true",
help="Slave模式下跳过等待host服务", help="Slave模式下跳过等待host服务",
) )
parser.add_argument(
"--upload_registry",
action="store_true",
help="启动unilab时同时报送注册表信息",
)
parser.add_argument( parser.add_argument(
"--config", "--config",
type=str, type=str,
@@ -97,22 +117,12 @@ def main():
# 加载配置文件优先加载config然后从env读取 # 加载配置文件优先加载config然后从env读取
config_path = args_dict.get("config") config_path = args_dict.get("config")
if config_path is None: load_config_from_file(config_path)
config_path = os.environ.get("UNILABOS.BASICCONFIG.CONFIG_PATH", None)
if config_path:
if not os.path.exists(config_path):
print_status(f"配置文件 {config_path} 不存在", "error")
elif not config_path.endswith(".py"):
print_status(f"配置文件 {config_path} 不是Python文件必须以.py结尾", "error")
else:
load_config(config_path)
else:
print_status(f"启动 UniLab-OS时配置文件参数未正确传入 --config '{config_path}' 尝试本地配置...", "warning")
load_config(config_path)
# 设置BasicConfig参数 # 设置BasicConfig参数
BasicConfig.is_host_mode = not args_dict.get("without_host", False) BasicConfig.is_host_mode = not args_dict.get("without_host", False)
BasicConfig.slave_no_host = args_dict.get("slave_no_host", False) BasicConfig.slave_no_host = args_dict.get("slave_no_host", False)
BasicConfig.upload_registry = args_dict.get("upload_registry", False)
machine_name = os.popen("hostname").read().strip() machine_name = os.popen("hostname").read().strip()
machine_name = "".join([c if c.isalnum() or c == "_" else "_" for c in machine_name]) machine_name = "".join([c if c.isalnum() or c == "_" else "_" for c in machine_name])
BasicConfig.machine_name = machine_name BasicConfig.machine_name = machine_name

View File

@@ -172,13 +172,14 @@ class MQTTClient:
jobdata = {"job_id": job_id, "data": feedback_data, "status": status, "return_info": return_info} jobdata = {"job_id": job_id, "data": feedback_data, "status": status, "return_info": return_info}
self.client.publish(f"labs/{MQConfig.lab_id}/job/list/", json.dumps(jobdata), qos=2) self.client.publish(f"labs/{MQConfig.lab_id}/job/list/", json.dumps(jobdata), qos=2)
def publish_registry(self, device_id: str, device_info: dict): def publish_registry(self, device_id: str, device_info: dict, print_debug: bool = True):
if self.mqtt_disable: if self.mqtt_disable:
return return
address = f"labs/{MQConfig.lab_id}/registry/" address = f"labs/{MQConfig.lab_id}/registry/"
registry_data = json.dumps({device_id: device_info}, ensure_ascii=False, cls=TypeEncoder) registry_data = json.dumps({device_id: device_info}, ensure_ascii=False, cls=TypeEncoder)
self.client.publish(address, registry_data, qos=2) self.client.publish(address, registry_data, qos=2)
logger.debug(f"Registry data published: address: {address}, {registry_data}") if print_debug:
logger.debug(f"Registry data published: address: {address}, {registry_data}")
def publish_actions(self, action_id: str, action_info: dict): def publish_actions(self, action_id: str, action_info: dict):
if self.mqtt_disable: if self.mqtt_disable:

67
unilabos/app/register.py Normal file
View File

@@ -0,0 +1,67 @@
import argparse
import time
from unilabos.registry.registry import build_registry
from unilabos.app.main import load_config_from_file
from unilabos.utils.log import logger
def register_devices_and_resources(mqtt_client, lab_registry):
"""
注册设备和资源到 MQTT
"""
logger.info("[UniLab Register] 开始注册设备和资源...")
# 注册设备信息
for device_info in lab_registry.obtain_registry_device_info():
mqtt_client.publish_registry(device_info["id"], device_info, False)
logger.debug(f"[UniLab Register] 注册设备: {device_info['id']}")
# 注册资源信息
for resource_info in lab_registry.obtain_registry_resource_info():
mqtt_client.publish_registry(resource_info["id"], resource_info, False)
logger.debug(f"[UniLab Register] 注册资源: {resource_info['id']}")
time.sleep(10)
logger.info("[UniLab Register] 设备和资源注册完成.")
def main():
"""
命令行入口函数
"""
parser = argparse.ArgumentParser(description="注册设备和资源到 MQTT")
parser.add_argument(
"--registry_path",
type=str,
default=None,
action="append",
help="注册表路径",
)
parser.add_argument(
"--config",
type=str,
default=None,
help="配置文件路径,支持.py格式的Python配置文件",
)
args = parser.parse_args()
# 构建注册表
build_registry(args.registry_path)
load_config_from_file(args.config)
from unilabos.app.mq import mqtt_client
# 连接mqtt
mqtt_client.start()
from unilabos.registry.registry import lab_registry
# 注册设备和资源
register_devices_and_resources(mqtt_client, lab_registry)
if __name__ == "__main__":
main()

View File

@@ -44,10 +44,10 @@ class HTTPClient:
f"{self.remote_addr}/lab/resource/edge/batch_create/?database_process_later={1 if database_process_later else 0}", f"{self.remote_addr}/lab/resource/edge/batch_create/?database_process_later={1 if database_process_later else 0}",
json=resources, json=resources,
headers={"Authorization": f"lab {self.auth}"}, headers={"Authorization": f"lab {self.auth}"},
timeout=5, timeout=100,
) )
if response.status_code != 200: if response.status_code != 200 and response.status_code != 201:
logger.error(f"添加物料关系失败: {response.text}") logger.error(f"添加物料关系失败: {response.status_code}, {response.text}")
return response return response
def resource_add(self, resources: List[Dict[str, Any]], database_process_later: bool) -> requests.Response: def resource_add(self, resources: List[Dict[str, Any]], database_process_later: bool) -> requests.Response:
@@ -64,7 +64,7 @@ class HTTPClient:
f"{self.remote_addr}/lab/resource/?database_process_later={1 if database_process_later else 0}", f"{self.remote_addr}/lab/resource/?database_process_later={1 if database_process_later else 0}",
json=resources, json=resources,
headers={"Authorization": f"lab {self.auth}"}, headers={"Authorization": f"lab {self.auth}"},
timeout=5, timeout=100,
) )
if response.status_code != 200: if response.status_code != 200:
logger.error(f"添加物料失败: {response.text}") logger.error(f"添加物料失败: {response.text}")
@@ -85,7 +85,7 @@ class HTTPClient:
f"{self.remote_addr}/lab/resource/?edge_format=1", f"{self.remote_addr}/lab/resource/?edge_format=1",
params={"id": id, "with_children": with_children}, params={"id": id, "with_children": with_children},
headers={"Authorization": f"lab {self.auth}"}, headers={"Authorization": f"lab {self.auth}"},
timeout=5, timeout=20,
) )
return response.json() return response.json()
@@ -103,7 +103,7 @@ class HTTPClient:
f"{self.remote_addr}/lab/resource/batch_delete/", f"{self.remote_addr}/lab/resource/batch_delete/",
params={"id": id}, params={"id": id},
headers={"Authorization": f"lab {self.auth}"}, headers={"Authorization": f"lab {self.auth}"},
timeout=5, timeout=20,
) )
return response return response
@@ -121,7 +121,7 @@ class HTTPClient:
f"{self.remote_addr}/lab/resource/batch_update/?edge_format=1", f"{self.remote_addr}/lab/resource/batch_update/?edge_format=1",
json=resources, json=resources,
headers={"Authorization": f"lab {self.auth}"}, headers={"Authorization": f"lab {self.auth}"},
timeout=5, timeout=100,
) )
return response return response

View File

@@ -10,8 +10,9 @@ from unilabos.utils import logger
class BasicConfig: class BasicConfig:
ENV = "pro" # 'test' ENV = "pro" # 'test'
config_path = "" config_path = ""
is_host_mode = True # 从registry.py移动过来 is_host_mode = True
slave_no_host = False # 是否跳过rclient.wait_for_service() slave_no_host = False # 是否跳过rclient.wait_for_service()
upload_registry = False
machine_name = "undefined" machine_name = "undefined"
vis_2d_enable = False vis_2d_enable = False

View File

@@ -48,12 +48,14 @@ solenoid_valve.mock:
feedback: {} feedback: {}
result: {} result: {}
handles: handles:
- handler_key: 0 - handler_key: in
label: 0 label: in
io_type: target
data_type: fluid data_type: fluid
side: NORTH side: NORTH
- handler_key: 1 - handler_key: out
label: 1 label: out
io_type: source
data_type: fluid data_type: fluid
side: SOUTH side: SOUTH
init_param_schema: init_param_schema:

View File

@@ -55,8 +55,21 @@
# 连接特性1个输入口混合液2个输出口上相和下相 # 连接特性1个输入口混合液2个输出口上相和下相
# 数据类型fluid流体连接 # 数据类型fluid流体连接
# 11. virtual_vacuum_pump - 虚拟真空泵
# 描述:真空泵设备,用于抽真空操作和真空/充气循环
# 连接特性1个输入口连接需要抽真空的系统
# 数据类型fluid流体连接
# 主要功能:开启/关闭、状态控制ON/OFF
# 12. virtual_gas_source - 虚拟气源
# 描述:气源设备,用于充气操作和真空/充气循环
# 连接特性1个输出口向系统提供加压气体
# 数据类型fluid流体连接
# 主要功能:开启/关闭、状态控制ON/OFF
virtual_pump: virtual_pump:
description: Virtual Pump for PumpTransferProtocol Testing description: Virtual Pump for PumpTransferProtocol Testing
#icon: 这个注册的设备应该是写错了,后续删掉
class: class:
module: unilabos.devices.virtual.virtual_pump:VirtualPump module: unilabos.devices.virtual.virtual_pump:VirtualPump
type: python type: python
@@ -94,10 +107,10 @@ virtual_pump:
success: success success: success
# 虚拟泵节点配置 - 具有多通道阀门特性根据valve_position可连接多个容器 # 虚拟泵节点配置 - 具有多通道阀门特性根据valve_position可连接多个容器
handles: handles:
- handler_key: pump-inlet - handler_key: pumpio
label: Pump Inlet label: pumpio
data_type: fluid data_type: fluid
io_type: target io_type: source
data_source: handle data_source: handle
data_key: fluid_in data_key: fluid_in
description: "泵的进液口,连接源容器" description: "泵的进液口,连接源容器"
@@ -114,6 +127,7 @@ virtual_pump:
virtual_stirrer: virtual_stirrer:
description: Virtual Stirrer for StirProtocol Testing description: Virtual Stirrer for StirProtocol Testing
icon: Stirrer.webp
class: class:
module: unilabos.devices.virtual.virtual_stirrer:VirtualStirrer module: unilabos.devices.virtual.virtual_stirrer:VirtualStirrer
type: python type: python
@@ -150,11 +164,11 @@ virtual_stirrer:
success: success success: success
# 虚拟搅拌器节点配置 - 机械连接设备,单一双向连接点 # 虚拟搅拌器节点配置 - 机械连接设备,单一双向连接点
handles: handles:
- handler_key: stirrer-vessel - handler_key: stirrer
label: Vessel Connection label: stirrer
data_type: mechanical data_type: mechanical
side: SOUTH side: NORTH
io_type: undirected io_type: source
data_source: handle data_source: handle
data_key: vessel data_key: vessel
description: "搅拌器的机械连接口,直接与反应容器连接提供搅拌功能" description: "搅拌器的机械连接口,直接与反应容器连接提供搅拌功能"
@@ -174,6 +188,7 @@ virtual_stirrer:
virtual_multiway_valve: virtual_multiway_valve:
description: Virtual 8-Way Valve for flow direction control description: Virtual 8-Way Valve for flow direction control
icon: EightPipeline.webp
class: class:
module: unilabos.devices.virtual.virtual_multiway_valve:VirtualMultiwayValve module: unilabos.devices.virtual.virtual_multiway_valve:VirtualMultiwayValve
type: python type: python
@@ -193,14 +208,15 @@ virtual_multiway_valve:
success: success success: success
# 八通阀门节点配置 - 1个输入口8个输出口可切换流向 # 八通阀门节点配置 - 1个输入口8个输出口可切换流向
handles: handles:
- handler_key: multiway-valve-inlet - handler_key: transferpump
label: Valve Inlet label: transferpump
data_type: fluid data_type: fluid
side: NORTH
io_type: target io_type: target
data_source: handle data_source: handle
data_key: fluid_in data_key: fluid_in
description: "八通阀门进液口,接收来源流体" description: "八通阀门进液口,接收来源流体"
- handler_key: multiway-valve-port-1 - handler_key: 1
label: 1 label: 1
data_type: fluid data_type: fluid
side: NORTH side: NORTH
@@ -208,7 +224,7 @@ virtual_multiway_valve:
data_source: executor data_source: executor
data_key: fluid_port_1 data_key: fluid_port_1
description: "八通阀门端口1position=1时流体从此口流出" description: "八通阀门端口1position=1时流体从此口流出"
- handler_key: multiway-valve-port-2 - handler_key: 2
label: 2 label: 2
data_type: fluid data_type: fluid
side: EAST side: EAST
@@ -216,7 +232,7 @@ virtual_multiway_valve:
data_source: executor data_source: executor
data_key: fluid_port_2 data_key: fluid_port_2
description: "八通阀门端口2position=2时流体从此口流出" description: "八通阀门端口2position=2时流体从此口流出"
- handler_key: multiway-valve-port-3 - handler_key: 3
label: 3 label: 3
data_type: fluid data_type: fluid
side: EAST side: EAST
@@ -224,7 +240,7 @@ virtual_multiway_valve:
data_source: executor data_source: executor
data_key: fluid_port_3 data_key: fluid_port_3
description: "八通阀门端口3position=3时流体从此口流出" description: "八通阀门端口3position=3时流体从此口流出"
- handler_key: multiway-valve-port-4 - handler_key: 4
label: 4 label: 4
data_type: fluid data_type: fluid
side: SOUTH side: SOUTH
@@ -232,7 +248,7 @@ virtual_multiway_valve:
data_source: executor data_source: executor
data_key: fluid_port_4 data_key: fluid_port_4
description: "八通阀门端口4position=4时流体从此口流出" description: "八通阀门端口4position=4时流体从此口流出"
- handler_key: multiway-valve-port-5 - handler_key: 5
label: 5 label: 5
data_type: fluid data_type: fluid
side: SOUTH side: SOUTH
@@ -240,7 +256,7 @@ virtual_multiway_valve:
data_source: executor data_source: executor
data_key: fluid_port_5 data_key: fluid_port_5
description: "八通阀门端口5position=5时流体从此口流出" description: "八通阀门端口5position=5时流体从此口流出"
- handler_key: multiway-valve-port-7 - handler_key: 7
label: 7 label: 7
data_type: fluid data_type: fluid
side: WEST side: WEST
@@ -248,7 +264,7 @@ virtual_multiway_valve:
data_source: executor data_source: executor
data_key: fluid_port_7 data_key: fluid_port_7
description: "八通阀门端口7position=7时流体从此口流出" description: "八通阀门端口7position=7时流体从此口流出"
- handler_key: multiway-valve-port-6 - handler_key: 6
label: 6 label: 6
data_type: fluid data_type: fluid
side: WEST side: WEST
@@ -256,7 +272,7 @@ virtual_multiway_valve:
data_source: executor data_source: executor
data_key: fluid_port_6 data_key: fluid_port_6
description: "八通阀门端口6position=6时流体从此口流出" description: "八通阀门端口6position=6时流体从此口流出"
- handler_key: multiway-valve-port-8 - handler_key: 8
label: 8 label: 8
data_type: fluid data_type: fluid
side: NORTH side: NORTH
@@ -276,6 +292,7 @@ virtual_multiway_valve:
additionalProperties: false additionalProperties: false
virtual_solenoid_valve: virtual_solenoid_valve:
description: Virtual Solenoid Valve for simple on/off flow control description: Virtual Solenoid Valve for simple on/off flow control
#icon: SolenoidValve.webp暂时还没有
class: class:
module: unilabos.devices.virtual.virtual_solenoid_valve:VirtualSolenoidValve module: unilabos.devices.virtual.virtual_solenoid_valve:VirtualSolenoidValve
type: python type: python
@@ -307,19 +324,21 @@ virtual_solenoid_valve:
success: success success: success
# 电磁阀门节点配置 - 双向流通的开关型阀门,流动方向由泵决定 # 电磁阀门节点配置 - 双向流通的开关型阀门,流动方向由泵决定
handles: handles:
- handler_key: solenoid-valve-port-in - handler_key: in
label: in label: in
data_type: fluid data_type: fluid
io_type: undirected side: NORTH
io_type: target
data_source: handle data_source: handle
data_key: fluid_port data_key: fluid_port_in
description: "电磁阀的双向流体口,开启时允许流体双向通过,关闭时完全阻断" description: "电磁阀的双向流体口,开启时允许流体双向通过,关闭时完全阻断"
- handler_key: solenoid-valve-port-out - handler_key: out
label: out label: out
data_type: fluid data_type: fluid
io_type: undirected side: SOUTH
io_type: source
data_source: handle data_source: handle
data_key: fluid_port data_key: fluid_port_out
description: "电磁阀的双向流体口,开启时允许流体双向通过,关闭时完全阻断" description: "电磁阀的双向流体口,开启时允许流体双向通过,关闭时完全阻断"
schema: schema:
type: object type: object
@@ -336,6 +355,7 @@ virtual_solenoid_valve:
additionalProperties: false additionalProperties: false
virtual_centrifuge: virtual_centrifuge:
description: Virtual Centrifuge for CentrifugeProtocol Testing description: Virtual Centrifuge for CentrifugeProtocol Testing
#icon: Centrifuge.webp暂时还没有
class: class:
module: unilabos.devices.virtual.virtual_centrifuge:VirtualCentrifuge module: unilabos.devices.virtual.virtual_centrifuge:VirtualCentrifuge
type: python type: python
@@ -368,10 +388,11 @@ virtual_centrifuge:
message: message message: message
# 虚拟离心机节点配置 - 单个样品处理设备,输入输出都是同一个样品容器 # 虚拟离心机节点配置 - 单个样品处理设备,输入输出都是同一个样品容器
handles: handles:
- handler_key: centrifuge-sample - handler_key: centrifuge
label: Sample Input/Output label: centrifuge
data_type: transport data_type: transport
io_type: undirected side: NORTH
io_type: target
data_source: handle data_source: handle
data_key: vessel data_key: vessel
description: "需要离心的样品容器" description: "需要离心的样品容器"
@@ -394,6 +415,7 @@ virtual_centrifuge:
virtual_filter: virtual_filter:
description: Virtual Filter for FilterProtocol Testing description: Virtual Filter for FilterProtocol Testing
icon: Filter.webp
class: class:
module: unilabos.devices.virtual.virtual_filter:VirtualFilter module: unilabos.devices.virtual.virtual_filter:VirtualFilter
type: python type: python
@@ -429,16 +451,16 @@ virtual_filter:
message: message message: message
# 虚拟过滤器节点配置 - 分离设备1个输入(原始样品)2个输出(滤液和滤渣) # 虚拟过滤器节点配置 - 分离设备1个输入(原始样品)2个输出(滤液和滤渣)
handles: handles:
- handler_key: filter-in - handler_key: filterin
label: Input label: filterin
data_type: fluid data_type: fluid
side: NORTH side: NORTH
io_type: target io_type: target
data_source: handle data_source: handle
data_key: vessel data_key: vessel
description: "需要过滤的原始样品容器" description: "需要过滤的原始样品容器"
- handler_key: filter-filtrate-out - handler_key: filtrate_out
label: Output label: filtrate_out
data_type: fluid data_type: fluid
side: SOUTH side: SOUTH
io_type: source io_type: source
@@ -469,6 +491,7 @@ virtual_filter:
virtual_heatchill: virtual_heatchill:
description: Virtual HeatChill for HeatChillProtocol Testing description: Virtual HeatChill for HeatChillProtocol Testing
icon: Heater.webp
class: class:
module: unilabos.devices.virtual.virtual_heatchill:VirtualHeatChill module: unilabos.devices.virtual.virtual_heatchill:VirtualHeatChill
type: python type: python
@@ -508,11 +531,11 @@ virtual_heatchill:
success: success success: success
# 虚拟加热/冷却器节点配置 - 温控设备,单一双向连接点用于放置容器 # 虚拟加热/冷却器节点配置 - 温控设备,单一双向连接点用于放置容器
handles: handles:
- handler_key: heatchill-vessel - handler_key: heatchill
label: Connection label: heatchill
data_type: mechanical data_type: mechanical
side: NORTH side: NORTH
io_type: undirected io_type: source
data_source: handle data_source: handle
data_key: vessel data_key: vessel
description: "加热/冷却器的物理连接口,容器直接放置在设备上进行温度控制" description: "加热/冷却器的物理连接口,容器直接放置在设备上进行温度控制"
@@ -535,6 +558,7 @@ virtual_heatchill:
virtual_transfer_pump: virtual_transfer_pump:
description: Virtual Transfer Pump for TransferProtocol Testing (Syringe-style) description: Virtual Transfer Pump for TransferProtocol Testing (Syringe-style)
icon: Pump.webp
class: class:
module: unilabos.devices.virtual.virtual_transferpump:VirtualPump module: unilabos.devices.virtual.virtual_transferpump:VirtualPump
type: python type: python
@@ -566,14 +590,14 @@ virtual_transfer_pump:
message: message message: message
# 注射器式转移泵节点配置 - 只有一个双向连接口,可吸入和排出液体 # 注射器式转移泵节点配置 - 只有一个双向连接口,可吸入和排出液体
handles: handles:
undirected: - handler_key: transferpump
- handler_key: syringe-port label: transferpump
label: Syringe Port data_type: fluid
data_type: fluid side: SOUTH
io_type: undirected io_type: source
data_source: handle data_source: handle
data_key: fluid_port data_key: fluid_port
description: "注射器式转移泵的唯一连接口,通过阀门切换实现吸入和排出" description: "注射器式转移泵的唯一连接口,通过阀门切换实现吸入和排出"
schema: schema:
type: object type: object
properties: properties:
@@ -590,6 +614,7 @@ virtual_transfer_pump:
virtual_column: virtual_column:
description: Virtual Column for RunColumn Protocol Testing description: Virtual Column for RunColumn Protocol Testing
#icon: Column.webp暂时还没有
class: class:
module: unilabos.devices.virtual.virtual_column:VirtualColumn module: unilabos.devices.virtual.virtual_column:VirtualColumn
type: python type: python
@@ -618,16 +643,16 @@ virtual_column:
message: message message: message
# 虚拟色谱柱节点配置 - 分离纯化设备1个样品输入口1个纯化产物输出口 # 虚拟色谱柱节点配置 - 分离纯化设备1个样品输入口1个纯化产物输出口
handles: handles:
- handler_key: column-sample-inlet - handler_key: columnin
label: Sample Input label: columnin
data_type: fluid data_type: fluid
side: NORTH side: NORTH
io_type: target io_type: target
data_source: handle data_source: handle
data_key: from_vessel data_key: from_vessel
description: "需要纯化的样品输入口" description: "需要纯化的样品输入口"
- handler_key: column-product-outlet - handler_key: columnout
label: Purified Product label: columnout
data_type: fluid data_type: fluid
side: SOUTH side: SOUTH
io_type: source io_type: source
@@ -653,6 +678,7 @@ virtual_column:
virtual_rotavap: virtual_rotavap:
description: Virtual Rotary Evaporator for EvaporateProtocol Testing description: Virtual Rotary Evaporator for EvaporateProtocol Testing
icon: Rotaryevaporator.webp
class: class:
module: unilabos.devices.virtual.virtual_rotavap:VirtualRotavap module: unilabos.devices.virtual.virtual_rotavap:VirtualRotavap
type: python type: python
@@ -685,32 +711,24 @@ virtual_rotavap:
result: result:
success: success success: success
message: message message: message
# 虚拟旋转蒸发仪节点配置 - 蒸发浓缩设备1个输入口(样品)2个输出口(浓缩物和冷凝液) # 虚拟旋转蒸发仪节点配置 - 1个双向口(样品进出)1个单向输出口(冷凝溶剂)
handles: handles:
- handler_key: rotavap-sample-inlet - handler_key: rotavap-sample
label: Sample Input label: rotavap-sample
data_type: fluid data_type: fluid
side: NORTH side: NORTH
io_type: target io_type: target
data_source: handle data_source: handle
data_key: vessel data_key: vessel
description: "需要蒸发的样品输入口" description: "样品的双向连接口,可放入需要蒸发的样品,蒸发完成后取出浓缩物"
- handler_key: rotavap-concentrate-outlet
label: Concentrate
data_type: fluid
side: SOUTH
io_type: source
data_source: executor
data_key: concentrate_vessel
description: "蒸发浓缩后的产物输出口"
- handler_key: rotavap-distillate-outlet - handler_key: rotavap-distillate-outlet
label: Distillate label: Distillate Outlet
data_type: fluid data_type: fluid
side: WEST side: WEST
io_type: source io_type: source
data_source: executor data_source: executor
data_key: distillate_vessel data_key: distillate_vessel
description: "冷凝回收的溶剂输出口" description: "冷凝回收的溶剂单向输出口,连接收集瓶"
schema: schema:
type: object type: object
properties: properties:
@@ -727,6 +745,7 @@ virtual_rotavap:
virtual_separator: virtual_separator:
description: Virtual Separator for SeparateProtocol Testing description: Virtual Separator for SeparateProtocol Testing
icon: Separator.webp
class: class:
module: unilabos.devices.virtual.virtual_separator:VirtualSeparator module: unilabos.devices.virtual.virtual_separator:VirtualSeparator
type: python type: python
@@ -765,24 +784,16 @@ virtual_separator:
message: message message: message
# 虚拟分液器节点配置 - 分离设备1个输入口(混合液)2个输出口(上相和下相) # 虚拟分液器节点配置 - 分离设备1个输入口(混合液)2个输出口(上相和下相)
handles: handles:
- handler_key: separator-inlet - handler_key: separatorin
label: Mixed Input label: separatorin
data_type: fluid data_type: fluid
side: NORTH side: NORTH
io_type: target io_type: target
data_source: handle data_source: handle
data_key: from_vessel data_key: from_vessel
description: "需要分离的混合液体输入口" description: "需要分离的混合液体输入口"
- handler_key: separator-top-outlet - handler_key: separatorout
label: Top Phase label: separatorout
data_type: fluid
side: EAST
io_type: source
data_source: executor
data_key: top_outlet
description: "上相(轻相)液体输出口"
- handler_key: separator-bottom-outlet
label: Bottom Phase
data_type: fluid data_type: fluid
side: SOUTH side: SOUTH
io_type: source io_type: source
@@ -805,6 +816,7 @@ virtual_separator:
virtual_vacuum_pump: virtual_vacuum_pump:
description: Virtual vacuum pump description: Virtual vacuum pump
icon: Vacuum.webp
class: class:
module: unilabos.devices.virtual.virtual_vacuum_pump:VirtualVacuumPump module: unilabos.devices.virtual.virtual_vacuum_pump:VirtualVacuumPump
type: python type: python
@@ -827,25 +839,28 @@ virtual_vacuum_pump:
string: string string: string
feedback: {} feedback: {}
result: {} result: {}
# 虚拟真空泵节点配置 - 真空设备1个输入口连接需要抽真空的系统
handles: handles:
- handler_key: out - handler_key: vacuumpump
label: out label: vacuumpump
data_type: fluid data_type: fluid
io_type: target side: SOUTH
data_source: handle io_type: source
data_key: fluid_in data_source: handle
init_param_schema: data_key: fluid_in
description: "真空泵进气口,连接需要抽真空的容器或管路"
schema:
type: object type: object
properties: properties:
port: port:
type: string type: string
description: "通信端口"
default: "VIRTUAL" default: "VIRTUAL"
required: description: "通信端口"
- port additionalProperties: false
virtual_gas_source: virtual_gas_source:
description: Virtual gas source description: Virtual gas source
#icon: GasSource.webp暂时还没有
class: class:
module: unilabos.devices.virtual.virtual_gas_source:VirtualGasSource module: unilabos.devices.virtual.virtual_gas_source:VirtualGasSource
type: python type: python
@@ -868,19 +883,29 @@ virtual_gas_source:
string: string string: string
feedback: {} feedback: {}
result: {} result: {}
# 虚拟气源节点配置 - 气体供应设备1个输出口提供加压气体
handles: handles:
- handler_key: out - handler_key: gassource
label: out label: gassource
data_type: fluid data_type: fluid
io_type: source side: SOUTH
data_source: executor io_type: source
data_key: fluid_out data_source: executor
init_param_schema: data_key: fluid_out
description: "气源出气口,向容器或管路提供加压气体"
schema:
type: object type: object
properties: properties:
port: port:
type: string type: string
description: "通信端口"
default: "VIRTUAL" default: "VIRTUAL"
required: description: "通信端口"
- port gas_type:
type: string
default: "nitrogen"
description: "气体类型"
max_pressure:
type: number
default: 5.0
description: "最大输出压力 (bar)"
additionalProperties: false

View File

@@ -6,9 +6,16 @@ container:
handles: handles:
- handler_key: top - handler_key: top
label: top label: top
io_type: target
data_type: fluid data_type: fluid
side: NORTH side: NORTH
- handler_key: bottom - handler_key: bottom
label: bottom label: bottom
io_type: source
data_type: fluid data_type: fluid
side: SOUTH
- handler_key: bind
label: bind
io_type: target
data_type: mechanical
side: SOUTH side: SOUTH

View File

@@ -175,8 +175,7 @@ def modify_to_backend_format(data: list[dict[str, Any]]) -> list[dict[str, Any]]
edge["targetHandle"] = port[target] edge["targetHandle"] = port[target]
elif "target_port" in edge: elif "target_port" in edge:
edge["targetHandle"] = edge.pop("target_port") edge["targetHandle"] = edge.pop("target_port")
if "id" not in edge: edge["id"] = f"reactflow__edge-{source}-{edge['sourceHandle']}-{target}-{edge['targetHandle']}"
edge["id"] = f"link_generated_{source}_{target}"
for key in ["source_port", "target_port"]: for key in ["source_port", "target_port"]:
if key in edge: if key in edge:
edge.pop(key) edge.pop(key)

View File

@@ -22,6 +22,7 @@ from unilabos_msgs.srv import (
) # type: ignore ) # type: ignore
from unique_identifier_msgs.msg import UUID from unique_identifier_msgs.msg import UUID
from unilabos.config.config import BasicConfig
from unilabos.registry.registry import lab_registry from unilabos.registry.registry import lab_registry
from unilabos.resources.graphio import initialize_resource from unilabos.resources.graphio import initialize_resource
from unilabos.resources.registry import add_schema from unilabos.resources.registry import add_schema
@@ -146,13 +147,15 @@ class HostNode(BaseROS2DeviceNode):
self.device_status = {} # 用来存储设备状态 self.device_status = {} # 用来存储设备状态
self.device_status_timestamps = {} # 用来存储设备状态最后更新时间 self.device_status_timestamps = {} # 用来存储设备状态最后更新时间
if BasicConfig.upload_registry:
from unilabos.app.mq import mqtt_client
from unilabos.app.mq import mqtt_client for device_info in lab_registry.obtain_registry_device_info():
mqtt_client.publish_registry(device_info["id"], device_info)
for device_info in lab_registry.obtain_registry_device_info(): for resource_info in lab_registry.obtain_registry_resource_info():
mqtt_client.publish_registry(device_info["id"], device_info) mqtt_client.publish_registry(resource_info["id"], resource_info)
for resource_info in lab_registry.obtain_registry_resource_info(): else:
mqtt_client.publish_registry(resource_info["id"], resource_info) self.lab_logger().warning("本次启动注册表不报送云端如果您需要联网调试请使用unilab-register命令进行单独报送或者在启动命令增加--upload_registry")
time.sleep(1) # 等待MQTT连接稳定 time.sleep(1) # 等待MQTT连接稳定
# 首次发现网络中的设备 # 首次发现网络中的设备
self._discover_devices() self._discover_devices()
@@ -195,18 +198,18 @@ class HostNode(BaseROS2DeviceNode):
resource_ids_to_instance = {i["id"]: i for i in resources_config} resource_ids_to_instance = {i["id"]: i for i in resources_config}
resource_name_to_with_parent_name = {} resource_name_to_with_parent_name = {}
for res in resources_config: for res in resources_config:
if res.get("parent") and res.get("type") == "device" and res.get("class"): # if res.get("parent") and res.get("type") == "device" and res.get("class"):
parent_id = res.get("parent") # parent_id = res.get("parent")
parent_res = resource_ids_to_instance[parent_id] # parent_res = resource_ids_to_instance[parent_id]
if parent_res.get("type") == "device" and parent_res.get("class"): # if parent_res.get("type") == "device" and parent_res.get("class"):
resource_with_parent_name.append(copy.deepcopy(res)) # resource_with_parent_name.append(copy.deepcopy(res))
resource_name_to_with_parent_name[resource_with_parent_name[-1]["id"]] = f"{parent_res['id']}/{res['id']}" # resource_name_to_with_parent_name[resource_with_parent_name[-1]["id"]] = f"{parent_res['id']}/{res['id']}"
resource_with_parent_name[-1]["id"] = f"{parent_res['id']}/{res['id']}" # resource_with_parent_name[-1]["id"] = f"{parent_res['id']}/{res['id']}"
continue # continue
resource_with_parent_name.append(copy.deepcopy(res)) resource_with_parent_name.append(copy.deepcopy(res))
for edge in self.resources_edge_config: # for edge in self.resources_edge_config:
edge["source"] = resource_name_to_with_parent_name.get(edge.get("source"), edge.get("source")) # edge["source"] = resource_name_to_with_parent_name.get(edge.get("source"), edge.get("source"))
edge["target"] = resource_name_to_with_parent_name.get(edge.get("target"), edge.get("target")) # edge["target"] = resource_name_to_with_parent_name.get(edge.get("target"), edge.get("target"))
try: try:
for bridge in self.bridges: for bridge in self.bridges:
if hasattr(bridge, "resource_add"): if hasattr(bridge, "resource_add"):

View File

@@ -110,7 +110,8 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
def initialize_device(self, device_id, device_config): def initialize_device(self, device_id, device_config):
"""初始化设备并创建相应的动作客户端""" """初始化设备并创建相应的动作客户端"""
device_id_abs = f"{self.device_id}/{device_id}" # device_id_abs = f"{self.device_id}/{device_id}"
device_id_abs = f"{device_id}"
self.lab_logger().info(f"初始化子设备: {device_id_abs}") self.lab_logger().info(f"初始化子设备: {device_id_abs}")
d = self.sub_devices[device_id] = initialize_device_from_dict(device_id_abs, device_config) d = self.sub_devices[device_id] = initialize_device_from_dict(device_id_abs, device_config)