Compare commits

...

3 Commits

Author SHA1 Message Date
ZiWei
b6b3d59083 feat(反应站): 添加反应器子设备支持
- 在设备注册表中添加反应器子设备配置
- 实现BioyondReactor类用于处理反应器数据
- 更新反应站主设备以支持子设备数据同步
- 在测试配置中添加5个反应器实例
2025-11-17 22:55:51 +08:00
ZiWei
f40e3f521c fix(camera): 修正摄像头配置,更新设备ID和UUID参数 2025-11-17 17:07:07 +08:00
Haohui
7cc2fe036f feat(main): enhance argument parsing for addr and port with priority handling 2025-11-16 22:53:54 +08:00
6 changed files with 211 additions and 9 deletions

View File

@@ -5,10 +5,16 @@
"name": "reaction_station_bioyond",
"parent": null,
"children": [
"Bioyond_Deck"
"Bioyond_Deck",
"reactor_1",
"reactor_2",
"reactor_3",
"reactor_4",
"reactor_5"
],
"type": "device",
"class": "reaction_station.bioyond",
"position": {"x": 0, "y": 3800, "z": 0},
"config": {
"config": {
"api_key": "DE9BDDA0",
@@ -64,6 +70,61 @@
},
"data": {}
},
{
"id": "reactor_1",
"name": "reactor_1",
"children": [],
"parent": "reaction_station_bioyond",
"type": "device",
"class": "reaction_station.reactor",
"position": {"x": 1150, "y": 380, "z": 0},
"config": {},
"data": {}
},
{
"id": "reactor_2",
"name": "reactor_2",
"children": [],
"parent": "reaction_station_bioyond",
"type": "device",
"class": "reaction_station.reactor",
"position": {"x": 1365, "y": 380, "z": 0},
"config": {},
"data": {}
},
{
"id": "reactor_3",
"name": "reactor_3",
"children": [],
"parent": "reaction_station_bioyond",
"type": "device",
"class": "reaction_station.reactor",
"position": {"x": 1580, "y": 380, "z": 0},
"config": {},
"data": {}
},
{
"id": "reactor_4",
"name": "reactor_4",
"children": [],
"parent": "reaction_station_bioyond",
"type": "device",
"class": "reaction_station.reactor",
"position": {"x": 1790, "y": 380, "z": 0},
"config": {},
"data": {}
},
{
"id": "reactor_5",
"name": "reactor_5",
"children": [],
"parent": "reaction_station_bioyond",
"type": "device",
"class": "reaction_station.reactor",
"position": {"x": 2010, "y": 380, "z": 0},
"config": {},
"data": {}
},
{
"id": "Bioyond_Deck",
"name": "Bioyond_Deck",

View File

@@ -105,7 +105,7 @@ def parse_args():
parser.add_argument(
"--port",
type=int,
default=8002,
default=None,
help="Port for web service information page",
)
parser.add_argument(
@@ -139,7 +139,7 @@ def parse_args():
parser.add_argument(
"--addr",
type=str,
default="https://uni-lab.bohrium.com/api/v1",
default=None,
help="Laboratory backend address",
)
parser.add_argument(
@@ -220,17 +220,53 @@ def main():
logger.info(f"Log level set to '{BasicConfig.log_level}' from config file.")
configure_logger(loglevel=BasicConfig.log_level)
if args_dict["addr"] == "test":
# 选择地址优先级:命令行 > 配置文件 > 默认线上
addr_cli = args_dict.get("addr", None)
addr_cfg = getattr(BasicConfig, "addr", None)
effective_addr = addr_cli if (addr_cli not in (None, "")) else addr_cfg
if effective_addr == "test":
print_status("使用测试环境地址", "info")
HTTPConfig.remote_addr = "https://uni-lab.test.bohrium.com/api/v1"
elif args_dict["addr"] == "uat":
elif effective_addr == "uat":
print_status("使用uat环境地址", "info")
HTTPConfig.remote_addr = "https://uni-lab.uat.bohrium.com/api/v1"
elif args_dict["addr"] == "local":
elif effective_addr == "local":
print_status("使用本地环境地址", "info")
HTTPConfig.remote_addr = "http://127.0.0.1:48197/api/v1"
elif effective_addr:
HTTPConfig.remote_addr = effective_addr
print_status(f"使用配置/命令行提供的自定义地址: {effective_addr}", "info")
else:
HTTPConfig.remote_addr = args_dict.get("addr", "")
# 默认地址
HTTPConfig.remote_addr = "https://uni-lab.bohrium.com/api/v1"
print_status("未提供地址,使用默认线上地址", "info")
# 选择端口优先级:命令行 > 配置文件 > 默认 8002
port_cli = args_dict.get("port", None)
port_cfg = getattr(BasicConfig, "port", None) if hasattr(BasicConfig, "port") else None
effective_port = port_cli if (port_cli is not None) else (port_cfg if (port_cfg is not None) else 8002)
args_dict["port"] = effective_port
if port_cli is not None:
print_status(f"使用命令行端口 {effective_port}", "info")
elif port_cfg is not None:
print_status(f"使用配置文件端口 {effective_port}", "info")
else:
print_status(f"未提供端口,使用默认端口 {effective_port}", "info")
# 选择是否打开浏览器:命令行(是否包含 --disable_browser) > 配置文件 > 默认 False
disable_browser_cli = "--disable_browser" in sys.argv
if disable_browser_cli:
args_dict["disable_browser"] = True
print_status("使用命令行设置:禁用浏览器", "info")
else:
disable_cfg = getattr(BasicConfig, "disable_browser", None)
if isinstance(disable_cfg, bool):
args_dict["disable_browser"] = disable_cfg
print_status(f"使用配置文件设置disable_browser={disable_cfg}", "info")
else:
args_dict["disable_browser"] = False
print_status("未提供 disable_browser默认开启浏览器", "info")
# 设置BasicConfig参数
if args_dict.get("ak", ""):

View File

@@ -14,6 +14,37 @@ from unilabos.devices.workstation.bioyond_studio.config import (
from unilabos.devices.workstation.bioyond_studio.config import API_CONFIG
class BioyondReactor:
def __init__(self, config: dict = None, deck=None, protocol_type=None, **kwargs):
self.in_temperature = 0.0
self.out_temperature = 0.0
self.pt100_temperature = 0.0
self.sensor_average_temperature = 0.0
self.target_temperature = 0.0
self.setting_temperature = 0.0
self.viscosity = 0.0
self.average_viscosity = 0.0
self.speed = 0.0
self.force = 0.0
def update_metrics(self, payload: Dict[str, Any]):
def _f(v):
try:
return float(v)
except Exception:
return 0.0
self.target_temperature = _f(payload.get("targetTemperature"))
self.setting_temperature = _f(payload.get("settingTemperature"))
self.in_temperature = _f(payload.get("inTemperature"))
self.out_temperature = _f(payload.get("outTemperature"))
self.pt100_temperature = _f(payload.get("pt100Temperature"))
self.sensor_average_temperature = _f(payload.get("sensorAverageTemperature"))
self.speed = _f(payload.get("speed"))
self.force = _f(payload.get("force"))
self.viscosity = _f(payload.get("viscosity"))
self.average_viscosity = _f(payload.get("averageViscosity"))
class BioyondReactionStation(BioyondWorkstation):
"""Bioyond反应站类
@@ -52,6 +83,8 @@ class BioyondReactionStation(BioyondWorkstation):
self.speed = 0.0
self.force = 0.0
self._frame_to_reactor_id = {1: "reactor_1", 2: "reactor_2", 3: "reactor_3", 4: "reactor_4", 5: "reactor_5"}
# ==================== 工作流方法 ====================
def reactor_taken_out(self):
@@ -558,6 +591,21 @@ class BioyondReactionStation(BioyondWorkstation):
pub = self._ros_node._property_publishers.get(name)
if pub:
pub.publish_property()
frame = data.get("frameCode")
reactor_id = None
try:
reactor_id = self._frame_to_reactor_id.get(int(frame))
except Exception:
reactor_id = None
if reactor_id and hasattr(self._ros_node, "sub_devices"):
child = self._ros_node.sub_devices.get(reactor_id)
if child and hasattr(child, "driver_instance"):
child.driver_instance.update_metrics(data)
pubs = getattr(child.ros_node_instance, "_property_publishers", {})
for name in props:
p = pubs.get(name)
if p:
p.publish_property()
except Exception:
pass
event = {
@@ -575,6 +623,7 @@ class BioyondReactionStation(BioyondWorkstation):
"averageViscosity": data.get("averageViscosity"),
"request_time": report_request.request_time,
"timestamp": datetime.now().isoformat(),
"reactor_id": self._frame_to_reactor_id.get(int(data.get("frameCode", 0))) if str(data.get("frameCode", "")).isdigit() else None,
}
base_dir = Path(__file__).resolve().parents[3] / "unilabos_data"

View File

@@ -1,4 +1,4 @@
camera.USB:
camera:
category:
- camera
class:

View File

@@ -565,3 +565,58 @@ reaction_station.bioyond:
- workstation_status
type: object
version: 1.0.0
reaction_station.reactor:
category:
- reactor
- reaction_station_bioyond
class:
action_value_mappings: {}
module: unilabos.devices.workstation.bioyond_studio.reaction_station:BioyondReactor
status_types:
average_viscosity: float
force: float
in_temperature: float
out_temperature: float
pt100_temperature: float
sensor_average_temperature: float
setting_temperature: float
speed: float
target_temperature: float
viscosity: float
type: python
config_info: []
description: 反应站子设备-反应器
handles: []
icon: reaction_station.webp
init_param_schema:
config:
properties:
config:
type: object
required: []
type: object
data:
properties:
average_viscosity:
type: number
force:
type: number
in_temperature:
type: number
out_temperature:
type: number
pt100_temperature:
type: number
sensor_average_temperature:
type: number
setting_temperature:
type: number
speed:
type: number
target_temperature:
type: number
viscosity:
type: number
required: []
type: object
version: 1.0.0

View File

@@ -6,12 +6,13 @@ from cv_bridge import CvBridge
from unilabos.ros.nodes.base_device_node import BaseROS2DeviceNode, DeviceNodeResourceTracker
class VideoPublisher(BaseROS2DeviceNode):
def __init__(self, device_id='video_publisher', camera_index=0, period: float = 0.1, resource_tracker: DeviceNodeResourceTracker = None):
def __init__(self, device_id='video_publisher', device_uuid='', camera_index=0, period: float = 0.1, resource_tracker: DeviceNodeResourceTracker = None):
# 初始化BaseROS2DeviceNode使用自身作为driver_instance
BaseROS2DeviceNode.__init__(
self,
driver_instance=self,
device_id=device_id,
device_uuid=device_uuid,
status_types={},
action_value_mappings={},
hardware_interface="camera",