6.7 KiB
变更说明 2026-03-24
问题背景
BioyondElectrolyteDeck(原 BIOYOND_YB_Deck)迁移后,前端物料未能正常上传/同步。
修复内容
1. unilabos/resources/bioyond/decks.py
- 补回
setup: bool = False参数及if setup: self.setup()逻辑,与旧版BIOYOND_YB_Deck保持一致 - 工厂函数
bioyond_electrolyte_deck保留显式调用deck.setup(),避免重复初始化
# 修复前(缺少 setup 参数,无法通过 setup=True 触发初始化)
def __init__(self, name, size_x, size_y, size_z, category):
super().__init__(...)
# 修复后
def __init__(self, name, size_x, size_y, size_z, category, setup: bool = False):
super().__init__(...)
if setup:
self.setup()
2. unilabos/resources/graphio.py
- 修复
resource_bioyond_to_plr中两处bottle.tracker.liquids直接赋值导致的崩溃 ResourceHolder(如枪头盒的 TipSpot 槽位)没有tracker属性,直接访问会抛出AttributeError,阻断整个 Bioyond 同步流程
# 修复前
bottle.tracker.liquids = [...]
# 修复后
if hasattr(bottle, 'tracker') and bottle.tracker is not None:
bottle.tracker.liquids = [...]
3. unilabos/app/main.py
- 保留
file_path is not None条件不变(已还原),并补充注释说明原因 - 该逻辑只在本地文件模式下有意义:本地 graph 文件只含设备结构,远端有已保存物料,merge 才能将两者合并
- 远端模式(
file_path=None)下,resource_tree_set和request_startup_json来自同一份数据,merge 为空操作,条件是否加file_path is not None对结果没有影响
4. unilabos/devices/workstation/bioyond_studio/station.py ⭐ 核心修复
- 当 deck 通过反序列化创建时,不会自动调用
setup(),导致deck.children为空,warehouses始终是{} - 增加兜底逻辑:仓库扫描后仍为空,则主动调用
deck.setup()初始化仓库 - 这是导致所有物料放置失败(
warehouse '...' 在deck中不存在。可用warehouses: [])的根本原因
# 新增兜底
if not self.deck.warehouses and hasattr(self.deck, "setup") and callable(self.deck.setup):
logger.info("Deck 无仓库子节点,调用 setup() 初始化仓库")
self.deck.setup()
补充修复 2026-03-25:依华扣电组装工站子物料未上传
问题
CoinCellAssemblyWorkstation.post_init 直接上传空 deck,未调用 deck.setup(),导致:
- 前端子物料(成品弹夹、料盘、瓶架等)不显示
- 运行时
self.deck.get_resource("成品弹夹")抛出ResourceNotFoundError
修复文件
unilabos/devices/workstation/coin_cell_assembly/YB_YH_materials.py
YihuaCoinCellDeck.__init__补回setup: bool = False参数及if setup: self.setup()逻辑
unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py
post_init中增加与 Bioyond 工站相同的兜底逻辑:deck 无子节点时调用deck.setup()初始化
# post_init 中新增
if self.deck and not self.deck.children and hasattr(self.deck, "setup") and callable(self.deck.setup):
logger.info("YihuaCoinCellDeck 无子节点,调用 setup() 初始化")
self.deck.setup()
联动 Bug:MaterialPlate.create_with_holes 构造顺序错误
现象:deck.setup() 被调用后,启动时抛出:
设备后初始化失败: Must specify either `ordered_items` or `ordering`.
根因:create_with_holes 原来的逻辑是先构造空的 MaterialPlate 实例,再 assign 洞位:
# 旧(错误):cls(...) 时 ordered_items=None → ItemizedResource.__init__ 立即报错
plate = cls(name=name, ...) # ← 这里就崩了
holes = create_ordered_items_2d(...) # ← 根本没走到这里
for hole_name, hole in holes.items():
plate.assign_child_resource(...)
pylabrobot 的 ItemizedResource.__init__ 强制要求 ordered_items 和 ordering 必须有一个不为 None,空构造直接失败。
修复:先建洞位,再作为 ordered_items 传给构造函数:
# 新(正确):先建洞位,再一次性传入构造函数
holes = create_ordered_items_2d(klass=MaterialHole, num_items_x=4, ...)
return cls(name=name, ..., ordered_items=holes)
此 bug 此前未被触发,是因为
deck.setup()从未被调用到——正是上面post_init兜底修复引出的联动问题。
补充修复 2026-03-25:3→2→1 转运资源同步失败
问题
配液工站(Bioyond)完成分液后,调用 transfer_3_to_2_to_1_auto 将分液瓶板转运到扣电工站(BatteryStation)。物理 LIMS 转运成功,但数字孪生资源树同步始终失败:
[资源同步] ❌ 失败: 目标设备 'BatteryStation' 中未找到资源 'bottle_rack_6x2'
根因
_get_resource_from_device 方法负责跨设备查找资源对象,有两个问题:
- 原始路径完全失效:尝试
from unilabos.app.ros2_app import get_device_plr_resource_by_name,但该模块不存在,ImportError被except Exception: pass静默吞掉 - 降级路径搜错地方:遍历
self._plr_resources(Bioyond 自己的资源),不可能找到 BatteryStation 的bottle_rack_6x2
修复文件
unilabos/devices/workstation/bioyond_studio/bioyond_cell/bioyond_cell_workstation.py
改用全局设备注册表 registered_devices 跨设备访问目标 deck:
# 修复前(失效)
from unilabos.app.ros2_app import get_device_plr_resource_by_name # 模块不存在
return get_device_plr_resource_by_name(device_id, resource_name)
# 修复后
from unilabos.ros.nodes.base_device_node import registered_devices
device_info = registered_devices.get(device_id)
if device_info is not None:
driver = device_info.get("driver_instance") # TypedDict 是 dict,必须用 .get()
if driver is not None:
deck = getattr(driver, "deck", None)
if deck is not None:
res = deck.get_resource(resource_name)
关键细节:DeviceInfoType 是 TypedDict(即普通 dict),必须用 device_info.get("driver_instance") 而非 getattr(device_info, "driver_instance", None)——后者对字典永远返回 None。
根本原因分析
旧版以本地文件模式启动(有 graph 文件),deck 在启动前已通过 merge_remote_resources 获得仓库子节点,反序列化时能正确恢复 warehouses。
新版以远端模式启动(file_path=None),deck 反序列化时没有仓库子节点,station.py 扫描为空,所有物料的 warehouse 匹配失败,Bioyond 同步的 16 个资源全部无法放置到对应仓库位,前端不显示。