fix(bioyond): port shared peptide station infrastructure to sirna

- update Bioyond reset RPCs and add take_out helper
- use scheduler status for Bioyond connection health checks
- align debug call log defaults
- add optional warehouse y-flip support
- register Bioyond deck resources for PLR deserialization
This commit is contained in:
yxz321
2026-05-25 15:11:44 +08:00
parent 45b9fd5262
commit 8e0807d11f
6 changed files with 109 additions and 32 deletions

View File

@@ -415,21 +415,24 @@ class BioyondV1RPC(BaseRequest):
return {}
return response.get("data", {})
def reset_location(self, location_id: str) -> int:
def reset_location(self, location_id: Optional[str] = None) -> int:
"""复位库位
现场实测 ``POST /api/lims/storage/reset-location`` 不传 ``data`` 即可成功
因此默认无 ``data`` 字段;保留 ``location_id`` 仅为兼容旧调用,传入会被忽略。
参数:
location_id: 库位ID
location_id: 兼容入参,已被忽略;新逻辑不再以 location 为粒度复位。
返回值:
int: 成功返回1失败返回0
"""
del location_id
response = self.post(
url=f'{self.host}/api/lims/storage/reset-location',
params={
"apiKey": self.api_key,
"requestTime": self.get_current_time_iso8601(),
"data": location_id,
})
if not response or response['code'] != 1:
return 0
@@ -779,6 +782,49 @@ class BioyondV1RPC(BaseRequest):
return response.get("data", {})
def take_out(
self,
order_id: str,
preintake_ids: list[str] | None = None,
material_ids: list[str] | None = None,
) -> dict:
"""取出订单关联通量/物料
参数:
order_id: 订单ID
preintake_ids: 通量ID列表可为空
material_ids: 物料ID列表可为空
返回值:
dict: 服务端响应包,失败返回空字典
"""
if not order_id:
self._logger.error("取出订单关联通量/物料错误: 缺少订单ID")
return {}
params = {
"orderId": order_id,
"preintakeIds": list(preintake_ids or []),
"materialIds": list(material_ids or []),
}
response = self.post(
url=f'{self.host}/api/lims/order/take-out',
params={
"apiKey": self.api_key,
"requestTime": self.get_current_time_iso8601(),
"data": params,
})
if not response:
return {}
if response['code'] != 1:
self._logger.error(f"取出订单关联通量/物料错误: {response.get('message', '')}")
return response
return response
def cancel_order(self, json_str: str) -> bool:
"""取消指定任务
@@ -886,21 +932,24 @@ class BioyondV1RPC(BaseRequest):
return {}
return response.get("data", {})
def reset_order_status(self, order_id: str) -> int:
def reset_order_status(self, order_id: Optional[str] = None) -> int:
"""复位订单状态
现场实测 ``POST /api/lims/order/reset-order-status`` 不传 ``data`` 即可成功
因此默认无 ``data`` 字段;保留 ``order_id`` 仅为兼容旧调用,传入会被忽略。
参数:
order_id: 订单ID
order_id: 兼容入参,已被忽略;新逻辑不再以单订单为粒度复位。
返回值:
int: 成功返回1失败返回0
"""
del order_id
response = self.post(
url=f'{self.host}/api/lims/order/reset-order-status',
params={
"apiKey": self.api_key,
"requestTime": self.get_current_time_iso8601(),
"data": order_id,
})
if not response or response['code'] != 1:
return 0

View File

@@ -8,7 +8,7 @@ returned. Outside of an active session the wrapped method delegates to the
original (unwrapped) implementation, leaving non-debug behavior intact.
The session writes a Markdown file under ``out_dir`` mirroring the format of
``temp_benyao/peptide/_logs/2026-04-30_160316_day3_samplefile_only_raw_calls.md``
``bioyond_debug_records/2026-04-30_160316_day3_samplefile_only_raw_calls.md``
minus the "Raw Payload Argument" section.
This module has no dependency on ``BioyondV1RPC`` itself; the only contract is

View File

@@ -56,13 +56,17 @@ class ConnectionMonitor:
def _monitor_loop(self):
while self._running:
try:
# 使用 lightweight API 检查连接
# query_matial_type_list 是比较快的查询
start_time = time.time()
result = self.workstation.hardware_interface.material_type_list()
# 使用轻量级调度状态接口检查连接,避免启动时打印完整物料类型列表。
result = self.workstation.hardware_interface.scheduler_status()
status = "online" if result else "offline"
msg = "Connection established" if status == "online" else "Failed to get material type list"
if status == "online":
msg = (
f"Scheduler status={result.get('status')}, "
f"hasTask={result.get('hasTask')}"
)
else:
msg = "Failed to get scheduler status"
if status != self._last_status:
logger.info(f"Bioyond连接状态变更: {self._last_status} -> {status}")
@@ -730,7 +734,7 @@ class BioyondWorkstation(WorkstationBase):
"""解析 ``debug_log_dir`` 为绝对路径。"""
configured = (getattr(self, "bioyond_config", {}) or {}).get("debug_log_dir")
default_dir = getattr(self, "_DEBUG_LOG_DEFAULT_DIR", None)
candidate = configured or default_dir or "temp_benyao/_logs/bioyond_debug"
candidate = configured or default_dir or "bioyond_debug_records"
path = Path(candidate)
if not path.is_absolute():
repo_root = Path(__file__).resolve().parents[4]

View File

@@ -1 +1,9 @@
from . import sirna_materials # noqa: F401 ensure @resource classes are importable for PLR deserialize
try:
from . import peptide_materials # noqa: F401 ensure @resource classes are importable for PLR deserialize
except Exception: # pragma: no cover - 允许轻量环境导入非资源辅助函数
peptide_materials = None # type: ignore[assignment]
try:
from . import sirna_materials # noqa: F401 ensure @resource classes are importable for PLR deserialize
except Exception: # pragma: no cover - 允许轻量环境导入非资源辅助函数
sirna_materials = None # type: ignore[assignment]

View File

@@ -25,6 +25,7 @@ def bioyond_warehouse_numeric_stack(
columns: int = 17,
bioyond_axis: str = "xy_row_col",
bioyond_key_axis: str = "row_col",
frontend_y_flip: bool = False,
) -> WareHouse:
"""创建 Bioyond 数字库位堆栈,库位名使用服务端返回的 行-列 格式。
@@ -46,17 +47,22 @@ def bioyond_warehouse_numeric_stack(
item_dx = 147.0
item_dy = 106.0
item_dz = 130.0
locations = [
Coordinate(dx + col * item_dx, dy + row * item_dy, dz)
for row in range(num_items_y)
for col in range(num_items_x)
]
resource_size_x = 127.0
resource_size_y = 86.0
resource_size_z = 25.0
size_y = dy + item_dy * num_items_y
locations = []
for row in range(num_items_y):
display_y = dy + row * item_dy
y = size_y - display_y - resource_size_y if frontend_y_flip else display_y
for col in range(num_items_x):
locations.append(Coordinate(dx + col * item_dx, y, dz))
holders = create_homogeneous_resources(
klass=ResourceHolder,
locations=locations,
resource_size_x=127.0,
resource_size_y=86.0,
resource_size_z=25.0,
resource_size_x=resource_size_x,
resource_size_y=resource_size_y,
resource_size_z=resource_size_z,
name_prefix=name,
)
if bioyond_key_axis == "row_col":
@@ -76,7 +82,7 @@ def bioyond_warehouse_numeric_stack(
warehouse = BioyondWareHouse(
name=name,
size_x=dx + item_dx * num_items_x,
size_y=dy + item_dy * num_items_y,
size_y=size_y,
size_z=dz + item_dz * num_items_z,
num_items_x=num_items_x,
num_items_y=num_items_y,
@@ -97,6 +103,7 @@ def bioyond_warehouse_live_grid(
slot_keys: list[str] | None = None,
bioyond_axis: str = "xy_col_row",
bioyond_key_axis: str = "row_col",
frontend_y_flip: bool = False,
) -> WareHouse:
"""创建 Bioyond 实测库位网格,按服务端 code 保存位点标签。
@@ -112,17 +119,22 @@ def bioyond_warehouse_live_grid(
item_dx = 147.0
item_dy = 106.0
item_dz = 130.0
locations = [
Coordinate(dx + col * item_dx, dy + row * item_dy, dz)
for row in range(num_items_y)
for col in range(num_items_x)
]
resource_size_x = 127.0
resource_size_y = 86.0
resource_size_z = 25.0
size_y = dy + item_dy * num_items_y
locations = []
for row in range(num_items_y):
display_y = dy + row * item_dy
y = size_y - display_y - resource_size_y if frontend_y_flip else display_y
for col in range(num_items_x):
locations.append(Coordinate(dx + col * item_dx, y, dz))
holders = create_homogeneous_resources(
klass=ResourceHolder,
locations=locations,
resource_size_x=127.0,
resource_size_y=86.0,
resource_size_z=25.0,
resource_size_x=resource_size_x,
resource_size_y=resource_size_y,
resource_size_z=resource_size_z,
name_prefix=name,
)
keys = slot_keys or [str(index + 1) for index in range(num_items_x * num_items_y)]
@@ -139,7 +151,7 @@ def bioyond_warehouse_live_grid(
return BioyondWareHouse(
name=name,
size_x=dx + item_dx * num_items_x,
size_y=dy + item_dy * num_items_y,
size_y=size_y,
size_z=dz + item_dz * num_items_z,
num_items_x=num_items_x,
num_items_y=num_items_y,

View File

@@ -18,3 +18,7 @@ def register():
from unilabos.devices.liquid_handling.rviz_backend import UniLiquidHandlerRvizBackend
from unilabos.devices.liquid_handling.laiyu.backend.laiyu_v_backend import UniLiquidHandlerLaiyuBackend
# noinspection PyUnresolvedReferences
from unilabos.resources.bioyond.decks import BIOYOND_SirnaStation_Deck
# noinspection PyUnresolvedReferences
from unilabos.resources.bioyond.decks import BIOYOND_PeptideStation_Deck