7 Commits

Author SHA1 Message Date
Calvin Cao
f5446c6480 Merge pull request #174 from sun7151887/yb_fix5
奔曜实现物料流
2025-11-25 18:39:56 +08:00
dijkstra402
a98d25c16d feat: expose workflow material outputs 2025-11-25 18:27:34 +08:00
Calvin Cao
80b9589973 Merge pull request #173 from sun7151887/yb_fix5
fix: 修复 BioyondCellWorkstation 和 CoinCellAssembly 工作流程
2025-11-25 18:26:26 +08:00
dijkstra402
4d4bbcbae8 fix: 修复 BioyondCellWorkstation 和 CoinCellAssembly 工作流程
- 修复 run 方法的函数参数语法错误(冒号改为等号)
- 将 BioyondCellWorkstation 的 run 函数移入类内部
- 添加 run_bioyond_cell_workflow 方法支持可选的 1to2 步骤
- 更新相关 YAML 配置文件
2025-11-25 15:39:07 +08:00
Calvin Cao
fa9b2a08f2 Merge pull request #171 from Andy6M/feat/merge-neware-battery-systems
feat: Merge Neware monitoring and submission systems into unified driver
2025-11-24 15:24:02 +08:00
Xie Qiming
929d50f954 feat: Merge Neware monitoring and submission systems into unified driver 2025-11-21 20:13:51 +08:00
calvincao
e60bf29a7f feat(workstation): 实现奔曜与扣电池装配工作流统一配置执行接口
- 新增 `run_bioyond_cell_workflow` 函数以支持通过配置驱动奔曜配液与转运流程
- 新增 `run_coin_cell_packaging_workflow` 函数以支持通过配置驱动扣电池装配流程
- 两个函数均接受字典配置参数,实现初始化、操作调用及日志记录等功能的灵活控制- 提供 keep_alive机制用于持续运行场景
- 更新主程序入口逻辑,使用新工作流函数替代原有手动调用方式
- 支持从配置中读取实验样本、调度器设置以及各项操作开关和日志选项- 添加对 Excel 订单创建路径的配置化支持- 引入路径对象处理文件输入,提升跨平台兼容性- 增强错误提示信息,确保必要字段如 create_orders 的 excel_path 存在
- 封装所有设备动作至标准化函数调用结构,便于维护和扩展
2025-11-19 09:51:24 +08:00
17 changed files with 2382 additions and 242 deletions

View File

@@ -1,29 +0,0 @@
{
"nodes": [
{
"id": "NEWARE_BATTERY_TEST_SYSTEM",
"name": "Neware Battery Test System",
"parent": null,
"type": "device",
"class": "neware_battery_test_system",
"position": {
"x": 620.6111111111111,
"y": 171,
"z": 0
},
"config": {
"ip": "127.0.0.1",
"port": 502,
"machine_id": 1,
"devtype": "27",
"timeout": 20,
"size_x": 500.0,
"size_y": 500.0,
"size_z": 2000.0
},
"data": {},
"children": []
}
],
"links": []
}

View File

@@ -0,0 +1,8 @@
from .neware_battery_test_system import NewareBatteryTestSystem
from .neware_driver import build_start_command, start_test
__all__ = [
"NewareBatteryTestSystem",
"build_start_command",
"start_test",
]

View File

@@ -0,0 +1,3 @@
Timestamp,Battery_Count,Assembly_Time,Open_Circuit_Voltage,Pole_Weight,Assembly_Pressure,Battery_Code,Electrolyte_Code,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʺ<EFBFBD><EFBFBD><EFBFBD>,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>mah/g,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϵ,<EFBFBD><EFBFBD><EFBFBD>,<EFBFBD>ź<EFBFBD>,ͨ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
2025/10/29 17:32,7,5,0.11299999803304672,18.049999237060547,3593,Li000595,Si-Gr001,9.2,0.954,469,SiGr_Li,1,1,2
2025/10/30 17:49,2,5,0,13.109999895095825,4094,YS101224,NoRead88,5.2,0.92,190,SiGr_Li,2,1,1
1 Timestamp Battery_Count Assembly_Time Open_Circuit_Voltage Pole_Weight Assembly_Pressure Battery_Code Electrolyte_Code 集流体质量 活性物质含量 克容量mah/g 电池体系 设备号 排号 通道号
2 2025/10/29 17:32 7 5 0.11299999803304672 18.049999237060547 3593 Li000595 Si-Gr001 9.2 0.954 469 SiGr_Li 1 1 2
3 2025/10/30 17:49 2 5 0 13.109999895095825 4094 YS101224 NoRead88 5.2 0.92 190 SiGr_Li 2 1 1

View File

@@ -0,0 +1,33 @@
{
"nodes": [
{
"id": "NEWARE_BATTERY_TEST_SYSTEM",
"name": "Neware Battery Test System",
"parent": null,
"type": "device",
"class": "neware_battery_test_system",
"position": {
"x": 620.0,
"y": 200.0,
"z": 0
},
"config": {
"ip": "127.0.0.1",
"port": 502,
"machine_id": 1,
"devtype": "27",
"timeout": 20,
"size_x": 500.0,
"size_y": 500.0,
"size_z": 2000.0
},
"data": {
"功能说明": "新威电池测试系统提供720通道监控和CSV批量提交功能",
"监控功能": "支持720个通道的实时状态监控、2盘电池物料管理、状态导出等",
"提交功能": "通过submit_from_csv action从CSV文件批量提交测试任务。CSV必须包含: Battery_Code, Pole_Weight, 集流体质量, 活性物质含量, 克容量mah/g, 电池体系, 设备号, 排号, 通道号"
},
"children": []
}
],
"links": []
}

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,8 @@
- 状态类型: working/stop/finish/protect/pause/false/unknown
"""
import os
import sys
import socket
import xml.etree.ElementTree as ET
import json
@@ -21,7 +23,6 @@ from dataclasses import dataclass
from typing import Any, Dict, List, Optional, TypedDict
from pylabrobot.resources import ResourceHolder, Coordinate, create_ordered_items_2d, Deck, Plate
from unilabos.ros.nodes.base_device_node import ROS2DeviceNode
from unilabos.ros.nodes.presets.workstation import ROS2WorkstationNode
@@ -56,13 +57,6 @@ class BatteryTestPositionState(TypedDict):
status: str # 通道状态
color: str # 状态对应颜色
# 额外的inquire协议字段
relativetime: float # 相对时间 (s)
open_or_close: int # 0=关闭, 1=打开
step_type: str # 步骤类型
cycle_id: int # 循环ID
step_id: int # 步骤ID
log_code: str # 日志代码
class BatteryTestPosition(ResourceHolder):
@@ -142,9 +136,9 @@ class NewareBatteryTestSystem:
devtype: str = None,
timeout: int = None,
size_x: float = 500.0,
size_y: float = 500.0,
size_z: float = 2000.0,
size_x: float = 50,
size_y: float = 50,
size_z: float = 20,
):
"""
初始化新威电池测试系统
@@ -162,6 +156,12 @@ class NewareBatteryTestSystem:
self.machine_id = machine_id
self.devtype = devtype or self.DEVTYPE
self.timeout = timeout or self.TIMEOUT
# 存储设备物理尺寸
self.size_x = size_x
self.size_y = size_y
self.size_z = size_z
self._last_status_update = None
self._cached_status = {}
self._ros_node: Optional[ROS2WorkstationNode] = None # ROS节点引用由框架设置
@@ -192,8 +192,9 @@ class NewareBatteryTestSystem:
def _setup_material_management(self):
"""设置物料管理系统"""
# 第1盘5行8列网格 (A1-E8) - 5行对应subdevid 1-58列对应chlid 1-8
# 先给物料设置一个最大的Deck
deck_main = Deck("ADeckName", 200, 200, 200)
# 先给物料设置一个最大的Deck,并设置其在空间中的位置
deck_main = Deck("ADeckName", 2000, 1800, 100, origin=Coordinate(2000,2000,0))
plate1_resources: Dict[str, BatteryTestPosition] = create_ordered_items_2d(
BatteryTestPosition,
@@ -202,8 +203,8 @@ class NewareBatteryTestSystem:
dx=10,
dy=10,
dz=0,
item_dx=45,
item_dy=45
item_dx=65,
item_dy=65
)
plate1 = Plate("P1", 400, 300, 50, ordered_items=plate1_resources)
deck_main.assign_child_resource(plate1, location=Coordinate(0, 0, 0))
@@ -232,11 +233,15 @@ class NewareBatteryTestSystem:
num_items_y=5, # 5行对应subdevid 6-10即A-E
dx=10,
dy=10,
dz=100, # Z轴偏移100mm
dz=0,
item_dx=65,
item_dy=65
)
plate2 = Plate("P2", 400, 300, 50, ordered_items=plate2_resources)
deck_main.assign_child_resource(plate2, location=Coordinate(0, 350, 0))
# 为第2盘资源添加P2_前缀
self.station_resources_plate2 = {}
for name, resource in plate2_resources.items():
@@ -306,55 +311,132 @@ class NewareBatteryTestSystem:
def _update_plate_resources(self, subunits: Dict):
"""更新两盘电池资源的状态"""
# 第1盘subdevid 1-5 映射到 P1_A1-P1_E8 (5行8列)
# 第1盘subdevid 1-5 映射到 8列5行网格 (列0-7, 行0-4)
for subdev_id in range(1, 6): # subdevid 1-5
status_row = subunits.get(subdev_id, {})
for chl_id in range(1, 9): # chlid 1-8
try:
# 计算在5×8网格中的位置
row_idx = (subdev_id - 1) # 0-4 (对应A-E)
col_idx = (chl_id - 1) # 0-7 (对应1-8)
resource_name = f"P1_{self.LETTERS[row_idx]}{col_idx + 1}"
# 根据用户描述:第一个是(0,0),最后一个是(7,4)
# 说明是8列5行列从0开始行从0开始
col_idx = (chl_id - 1) # 0-7 (chlid 1-8 -> 列0-7)
row_idx = (subdev_id - 1) # 0-4 (subdevid 1-5 -> 行0-4)
# 尝试多种可能的资源命名格式
possible_names = [
f"P1_batterytestposition_{col_idx}_{row_idx}", # 用户提到的格式
f"P1_{self.LETTERS[row_idx]}{col_idx + 1}", # 原有的A1-E8格式
f"P1_{self.LETTERS[row_idx].lower()}{col_idx + 1}", # 小写字母格式
]
r = None
resource_name = None
for name in possible_names:
if name in self.station_resources:
r = self.station_resources[name]
resource_name = name
break
r = self.station_resources.get(resource_name)
if r:
status_channel = status_row.get(chl_id, {})
metrics = status_channel.get("metrics", {})
# 构建BatteryTestPosition状态数据移除capacity和energy
channel_state = {
# 基本测量数据
"voltage": metrics.get("voltage_V", 0.0),
"current": metrics.get("current_A", 0.0),
"time": metrics.get("totaltime_s", 0.0),
# 状态信息
"status": status_channel.get("state", "unknown"),
"color": status_channel.get("color", self.STATUS_COLOR["unknown"]),
"voltage": status_channel.get("voltage_V", 0.0),
"current": status_channel.get("current_A", 0.0),
"time": status_channel.get("totaltime_s", 0.0),
# 通道名称标识
"Channel_Name": f"{self.machine_id}-{subdev_id}-{chl_id}",
}
r.load_state(channel_state)
except (KeyError, IndexError):
# 调试信息
if self._ros_node and hasattr(self._ros_node, 'lab_logger'):
self._ros_node.lab_logger().debug(
f"更新P1资源状态: {resource_name} <- subdev{subdev_id}/chl{chl_id} "
f"状态:{channel_state['status']}"
)
else:
# 如果找不到资源,记录调试信息
if self._ros_node and hasattr(self._ros_node, 'lab_logger'):
self._ros_node.lab_logger().debug(
f"P1未找到资源: subdev{subdev_id}/chl{chl_id} -> 尝试的名称: {possible_names}"
)
except (KeyError, IndexError) as e:
if self._ros_node and hasattr(self._ros_node, 'lab_logger'):
self._ros_node.lab_logger().debug(f"P1映射错误: subdev{subdev_id}/chl{chl_id} - {e}")
continue
# 第2盘subdevid 6-10 映射到 P2_A1-P2_E8 (5行8列)
# 第2盘subdevid 6-10 映射到 8列5行网格 (列0-7, 行0-4)
for subdev_id in range(6, 11): # subdevid 6-10
status_row = subunits.get(subdev_id, {})
for chl_id in range(1, 9): # chlid 1-8
try:
# 计算在5×8网格中的位置
row_idx = (subdev_id - 6) # 0-4 (subdevid 6->0, 7->1, ..., 10->4) (对应A-E)
col_idx = (chl_id - 1) # 0-7 (对应1-8)
resource_name = f"P2_{self.LETTERS[row_idx]}{col_idx + 1}"
col_idx = (chl_id - 1) # 0-7 (chlid 1-8 -> 列0-7)
row_idx = (subdev_id - 6) # 0-4 (subdevid 6-10 -> 行0-4)
# 尝试多种可能的资源命名格式
possible_names = [
f"P2_batterytestposition_{col_idx}_{row_idx}", # 用户提到的格式
f"P2_{self.LETTERS[row_idx]}{col_idx + 1}", # 原有的A1-E8格式
f"P2_{self.LETTERS[row_idx].lower()}{col_idx + 1}", # 小写字母格式
]
r = None
resource_name = None
for name in possible_names:
if name in self.station_resources:
r = self.station_resources[name]
resource_name = name
break
r = self.station_resources.get(resource_name)
if r:
status_channel = status_row.get(chl_id, {})
metrics = status_channel.get("metrics", {})
# 构建BatteryTestPosition状态数据移除capacity和energy
channel_state = {
# 基本测量数据
"voltage": metrics.get("voltage_V", 0.0),
"current": metrics.get("current_A", 0.0),
"time": metrics.get("totaltime_s", 0.0),
# 状态信息
"status": status_channel.get("state", "unknown"),
"color": status_channel.get("color", self.STATUS_COLOR["unknown"]),
"voltage": status_channel.get("voltage_V", 0.0),
"current": status_channel.get("current_A", 0.0),
"time": status_channel.get("totaltime_s", 0.0),
# 通道名称标识
"Channel_Name": f"{self.machine_id}-{subdev_id}-{chl_id}",
}
r.load_state(channel_state)
except (KeyError, IndexError):
# 调试信息
if self._ros_node and hasattr(self._ros_node, 'lab_logger'):
self._ros_node.lab_logger().debug(
f"更新P2资源状态: {resource_name} <- subdev{subdev_id}/chl{chl_id} "
f"状态:{channel_state['status']}"
)
else:
# 如果找不到资源,记录调试信息
if self._ros_node and hasattr(self._ros_node, 'lab_logger'):
self._ros_node.lab_logger().debug(
f"P2未找到资源: subdev{subdev_id}/chl{chl_id} -> 尝试的名称: {possible_names}"
)
except (KeyError, IndexError) as e:
if self._ros_node and hasattr(self._ros_node, 'lab_logger'):
self._ros_node.lab_logger().debug(f"P2映射错误: subdev{subdev_id}/chl{chl_id} - {e}")
continue
ROS2DeviceNode.run_async_func(self._ros_node.update_resource, True, **{
"resources": list(self.station_resources.values())
})
@property
def connection_info(self) -> Dict[str, str]:
@@ -490,6 +572,45 @@ class NewareBatteryTestSystem:
def debug_resource_names(self) -> dict:
"""
调试方法显示所有资源的实际名称ROS2动作
Returns:
dict: ROS2动作结果格式包含所有资源名称信息
"""
try:
debug_info = {
"total_resources": len(self.station_resources),
"plate1_resources": len(self.station_resources_plate1),
"plate2_resources": len(self.station_resources_plate2),
"plate1_names": list(self.station_resources_plate1.keys())[:10], # 显示前10个
"plate2_names": list(self.station_resources_plate2.keys())[:10], # 显示前10个
"all_resource_names": list(self.station_resources.keys())[:20], # 显示前20个
}
# 检查是否有用户提到的命名格式
batterytestposition_names = [name for name in self.station_resources.keys()
if "batterytestposition" in name]
debug_info["batterytestposition_names"] = batterytestposition_names[:10]
success_msg = f"资源调试信息获取成功,共{debug_info['total_resources']}个资源"
if self._ros_node:
self._ros_node.lab_logger().info(success_msg)
self._ros_node.lab_logger().info(f"调试信息: {debug_info}")
return {
"return_info": success_msg,
"success": True,
"debug_data": debug_info
}
except Exception as e:
error_msg = f"获取资源调试信息失败: {str(e)}"
if self._ros_node:
self._ros_node.lab_logger().error(error_msg)
return {"return_info": error_msg, "success": False}
# ========================
# 辅助方法
# ========================
@@ -538,6 +659,228 @@ class NewareBatteryTestSystem:
except Exception as e:
print(f" 获取状态失败: {e}")
# ========================
# CSV批量提交功能新增
# ========================
def _ensure_local_import_path(self):
"""确保本地模块导入路径"""
base_dir = os.path.dirname(__file__)
if base_dir not in sys.path:
sys.path.insert(0, base_dir)
def _canon(self, bs: str) -> str:
"""规范化电池体系名称"""
return str(bs).strip().replace('-', '_').upper()
def _compute_values(self, row):
"""
计算活性物质质量和容量
Args:
row: DataFrame行数据
Returns:
tuple: (活性物质质量mg, 容量mAh)
"""
pw = float(row['Pole_Weight'])
cm = float(row['集流体质量'])
am = row['活性物质含量']
if isinstance(am, str) and am.endswith('%'):
amv = float(am.rstrip('%')) / 100.0
else:
amv = float(am)
act_mass = (pw - cm) * amv
sc = float(row['克容量mah/g'])
cap = act_mass * sc / 1000.0
return round(act_mass, 2), round(cap, 3)
def _get_xml_builder(self, gen_mod, key: str):
"""
获取对应电池体系的XML生成函数
Args:
gen_mod: generate_xml_content模块
key: 电池体系标识
Returns:
callable: XML生成函数
"""
fmap = {
'LB6': gen_mod.xml_LB6,
'GR_LI': gen_mod.xml_Gr_Li,
'LFP_LI': gen_mod.xml_LFP_Li,
'LFP_GR': gen_mod.xml_LFP_Gr,
'811_LI_002': gen_mod.xml_811_Li_002,
'811_LI_005': gen_mod.xml_811_Li_005,
'SIGR_LI_STEP': gen_mod.xml_SiGr_Li_Step,
'SIGR_LI': gen_mod.xml_SiGr_Li_Step,
'811_SIGR': gen_mod.xml_811_SiGr,
}
if key not in fmap:
raise ValueError(f"未定义电池体系映射: {key}")
return fmap[key]
def _save_xml(self, xml: str, path: str):
"""
保存XML文件
Args:
xml: XML内容
path: 文件路径
"""
with open(path, 'w', encoding='utf-8') as f:
f.write(xml)
def submit_from_csv(self, csv_path: str, output_dir: str = ".") -> dict:
"""
从CSV文件批量提交Neware测试任务设备动作
Args:
csv_path (str): 输入CSV文件路径
output_dir (str): 输出目录用于存储XML文件和备份默认当前目录
Returns:
dict: 执行结果 {"return_info": str, "success": bool, "submitted_count": int}
"""
try:
# 确保可以导入本地模块
self._ensure_local_import_path()
import pandas as pd
import generate_xml_content as gen_mod
from neware_driver import start_test
if self._ros_node:
self._ros_node.lab_logger().info(f"开始从CSV文件提交任务: {csv_path}")
# 读取CSV文件
if not os.path.exists(csv_path):
error_msg = f"CSV文件不存在: {csv_path}"
if self._ros_node:
self._ros_node.lab_logger().error(error_msg)
return {"return_info": error_msg, "success": False, "submitted_count": 0}
df = pd.read_csv(csv_path, encoding='gbk')
# 验证必需列
required = [
'Battery_Code', 'Pole_Weight', '集流体质量', '活性物质含量',
'克容量mah/g', '电池体系', '设备号', '排号', '通道号'
]
missing = [c for c in required if c not in df.columns]
if missing:
error_msg = f"CSV缺少必需列: {missing}"
if self._ros_node:
self._ros_node.lab_logger().error(error_msg)
return {"return_info": error_msg, "success": False, "submitted_count": 0}
# 创建输出目录
xml_dir = os.path.join(output_dir, 'xml_dir')
backup_dir = os.path.join(output_dir, 'backup_dir')
os.makedirs(xml_dir, exist_ok=True)
os.makedirs(backup_dir, exist_ok=True)
if self._ros_node:
self._ros_node.lab_logger().info(
f"输出目录: XML={xml_dir}, 备份={backup_dir}"
)
# 逐行处理CSV数据
submitted_count = 0
results = []
for idx, row in df.iterrows():
try:
coin_id = str(row['Battery_Code'])
# 计算活性物质质量和容量
act_mass, cap_mAh = self._compute_values(row)
if cap_mAh < 0:
error_msg = (
f"容量为负数: Battery_Code={coin_id}, "
f"活性物质质量mg={act_mass}, 容量mah={cap_mAh}"
)
if self._ros_node:
self._ros_node.lab_logger().warning(error_msg)
results.append(f"{idx+1} 失败: {error_msg}")
continue
# 获取电池体系对应的XML生成函数
key = self._canon(row['电池体系'])
builder = self._get_xml_builder(gen_mod, key)
# 生成XML内容
xml_content = builder(act_mass, cap_mAh)
# 获取设备信息
devid = int(row['设备号'])
subdevid = int(row['排号'])
chlid = int(row['通道号'])
# 保存XML文件
recipe_path = os.path.join(
xml_dir,
f"{coin_id}_{devid}_{subdevid}_{chlid}.xml"
)
self._save_xml(xml_content, recipe_path)
# 提交测试任务
resp = start_test(
ip=self.ip,
port=self.port,
devid=devid,
subdevid=subdevid,
chlid=chlid,
CoinID=coin_id,
recipe_path=recipe_path,
backup_dir=backup_dir
)
submitted_count += 1
results.append(f"{idx+1} {coin_id}: {resp}")
if self._ros_node:
self._ros_node.lab_logger().info(
f"已提交 {coin_id} (设备{devid}-{subdevid}-{chlid}): {resp}"
)
except Exception as e:
error_msg = f"{idx+1} 处理失败: {str(e)}"
results.append(error_msg)
if self._ros_node:
self._ros_node.lab_logger().error(error_msg)
# 汇总结果
success_msg = (
f"批量提交完成: 成功{submitted_count}个,共{len(df)}行。"
f"\n详细结果:\n" + "\n".join(results)
)
if self._ros_node:
self._ros_node.lab_logger().info(
f"批量提交完成: 成功{submitted_count}/{len(df)}"
)
return {
"return_info": success_msg,
"success": True,
"submitted_count": submitted_count,
"total_count": len(df),
"results": results
}
except Exception as e:
error_msg = f"批量提交失败: {str(e)}"
if self._ros_node:
self._ros_node.lab_logger().error(error_msg)
return {
"return_info": error_msg,
"success": False,
"submitted_count": 0
}
def get_device_summary(self) -> dict:
"""
获取设备级别的摘要统计设备动作

View File

@@ -0,0 +1,49 @@
import socket
END_MARKS = [b"\r\n#\r\n", b"</bts>"] # 读到任一标志即可判定完整响应
def build_start_command(devid, subdevid, chlid, CoinID,
ip_in_xml="127.0.0.1",
devtype:int=27,
recipe_path:str=f"D:\\HHM_test\\A001.xml",
backup_dir:str=f"D:\\HHM_test\\backup") -> str:
lines = [
'<?xml version="1.0" encoding="UTF-8"?>',
'<bts version="1.0">',
' <cmd>start</cmd>',
' <list count="1">',
f' <start ip="{ip_in_xml}" devtype="{devtype}" devid="{devid}" subdevid="{subdevid}" chlid="{chlid}" barcode="{CoinID}">{recipe_path}</start>',
f' <backup backupdir="{backup_dir}" remotedir="" filenametype="1" customfilename="" createdirbydate="0" filetype="0" backupontime="1" backupontimeinterval="1" backupfree="0" />',
' </list>',
'</bts>',
]
# TCP 模式:请求必须以 #\r\n 结束(协议要求)
return "\r\n".join(lines) + "\r\n#\r\n"
def recv_until_marks(sock: socket.socket, timeout=60):
sock.settimeout(timeout) # 上限给足,协议允许到 30s:contentReference[oaicite:2]{index=2}
buf = bytearray()
while True:
chunk = sock.recv(8192)
if not chunk:
break
buf += chunk
# 读到结束标志就停,避免等对端断开
for m in END_MARKS:
if m in buf:
return bytes(buf)
# 保险:读到完整 XML 结束标签也停
if b"</bts>" in buf:
return bytes(buf)
return bytes(buf)
def start_test(ip="127.0.0.1", port=502, devid=3, subdevid=2, chlid=1, CoinID="A001", recipe_path=f"D:\\HHM_test\\A001.xml", backup_dir=f"D:\\HHM_test\\backup"):
xml_cmd = build_start_command(devid=devid, subdevid=subdevid, chlid=chlid, CoinID=CoinID, recipe_path=recipe_path, backup_dir=backup_dir)
#print(xml_cmd)
with socket.create_connection((ip, port), timeout=60) as s:
s.sendall(xml_cmd.encode("utf-8"))
data = recv_until_marks(s, timeout=60)
return data.decode("utf-8", errors="replace")
if __name__ == "__main__":
resp = start_test(ip="127.0.0.1", port=502, devid=4, subdevid=10, chlid=1, CoinID="A001", recipe_path=f"D:\\HHM_test\\A001.xml", backup_dir=f"D:\\HHM_test\\backup")
print(resp)

View File

@@ -19,6 +19,7 @@ from unilabos.devices.workstation.bioyond_studio.config import (
)
from unilabos.devices.workstation.workstation_http_service import WorkstationHTTPService
from unilabos.resources.bioyond.decks import BIOYOND_YB_Deck
from unilabos.resources.graphio import resource_bioyond_to_plr
from unilabos.utils.log import logger
from unilabos.registry.registry import lab_registry
@@ -257,7 +258,7 @@ class BioyondCellWorkstation(BioyondWorkstation):
def auto_feeding4to3(
self,
# ★ 修改点:默认模板路径
xlsx_path: Optional[str] = "/Users/calvincao/Desktop/work/uni-lab-all/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx",
xlsx_path: Optional[str] = "/Users/sml/work/Unilab/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx",
# ---------------- WH4 - 加样头面 (Z=1, 12个点位) ----------------
WH4_x1_y1_z1_1_materialName: str = "", WH4_x1_y1_z1_1_quantity: float = 0.0,
WH4_x2_y1_z1_2_materialName: str = "", WH4_x2_y1_z1_2_quantity: float = 0.0,
@@ -394,10 +395,13 @@ class BioyondCellWorkstation(BioyondWorkstation):
order_code = response.get("data", {}).get("orderCode")
if not order_code:
logger.error("上料任务未返回有效 orderCode")
return response
# 等待完成报送
return {"api_response": response, "order_finish": None}
# 等待完成报送
result = self.wait_for_order_finish(order_code)
return result
return {
"api_response": response,
"order_finish": result
}
def auto_batch_outbound_from_xlsx(self, xlsx_path: str) -> Dict[str, Any]:
@@ -477,7 +481,7 @@ class BioyondCellWorkstation(BioyondWorkstation):
- totalMass 自动计算为所有物料质量之和
- createTime 缺失或为空时自动填充为当前日期YYYY/M/D
"""
default_path = Path("/Users/calvincao/Desktop/work/uni-lab-all/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/2025092701.xlsx")
default_path = Path("/Users/sml/work/Unilab/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/2025092701.xlsx")
path = Path(xlsx_path) if xlsx_path else default_path
print(f"[create_orders] 使用 Excel 路径: {path}")
if path != default_path:
@@ -623,7 +627,7 @@ class BioyondCellWorkstation(BioyondWorkstation):
if not order_code:
logger.error("上料任务未返回有效 orderCode")
return response
# 等待完成报送
# 等待完成报送
result = self.wait_for_order_finish(order_code)
return result
@@ -1165,31 +1169,94 @@ class BioyondCellWorkstation(BioyondWorkstation):
})
return final_result
def _fetch_bioyond_materials(
self,
*,
filter_keyword: Optional[str] = None,
type_mode: int = 2,
) -> List[Dict[str, Any]]:
query: Dict[str, Any] = {
"typeMode": type_mode,
"includeDetail": True,
}
if filter_keyword:
query["filter"] = filter_keyword
response = self._post_lims("/api/lims/storage/stock-material", query)
raw_materials = response.get("data")
if not isinstance(raw_materials, list):
raw_materials = []
try:
resource_bioyond_to_plr(
raw_materials,
type_mapping=self.bioyond_config.get("material_type_mappings", MATERIAL_TYPE_MAPPINGS),
deck=self.deck,
)
except Exception as exc:
logger.warning(f"转换奔曜物料到 PLR 失败: {exc}", exc_info=True)
return raw_materials
def run_feeding_stage(self) -> Dict[str, List[Dict[str, Any]]]:
self.create_sample(
board_type="配液瓶(小)板",
bottle_type="配液瓶(小)",
location_code="B01",
name="配液瓶",
warehouse_name="手动堆栈"
)
self.create_sample(
board_type="5ml分液瓶板",
bottle_type="5ml分液瓶",
location_code="B02",
name="分液瓶",
warehouse_name="手动堆栈"
)
self.scheduler_start()
self.auto_feeding4to3(
xlsx_path="/Users/sml/work/Unilab/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx"
)
feeding_materials = self._fetch_bioyond_materials()
return {"feeding_materials": feeding_materials}
def run_liquid_preparation_stage(
self,
feeding_materials: Optional[List[Dict[str, Any]]] = None,
) -> Dict[str, List[Dict[str, Any]]]:
result = self.create_orders(
xlsx_path="/Users/sml/work/Unilab/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/2025092701.xlsx"
)
filter_keyword = self.bioyond_config.get("mixing_material_filter") or None
materials = result.get("materials")
if materials is None:
materials = self._fetch_bioyond_materials(filter_keyword=filter_keyword)
return {
"feeding_materials": feeding_materials or [],
"liquid_materials": materials,
}
def run_transfer_stage(
self,
liquid_materials: Optional[List[Dict[str, Any]]] = None,
) -> Dict[str, List[Dict[str, Any]]]:
self.transfer_3_to_2_to_1(
source_wh_id="3a19debc-84b4-0359-e2d4-b3beea49348b",
source_x=1,
source_y=1,
source_z=1,
)
transfer_materials = self._fetch_bioyond_materials()
return {
"liquid_materials": liquid_materials or [],
"transfer_materials": transfer_materials,
}
if __name__ == "__main__":
lab_registry.setup()
deck = BIOYOND_YB_Deck(setup=True)
ws = BioyondCellWorkstation(deck=deck)
# ws.update_push_ip() #直接修改奔耀端的报送ip地址
# ws.create_sample(name="配液瓶", board_type="配液瓶(小)板", bottle_type="配液瓶(小)", location_code="E01")
# ws.create_sample(name="分液瓶", board_type="5ml分液瓶板", bottle_type="5ml分液瓶", location_code="D01")
# # logger.info(ws.scheduler_stop())
# logger.info(ws.scheduler_start())
# logger.info(ws.auto_feeding4to3()) #搬运物料到3号箱
# 使用正斜杠或 Path 对象来指定文件路径
# excel_path = Path("/Users/calvincao/Desktop/work/uni-lab-all/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/2025092701.xlsx")
# logger.info(ws.create_orders(excel_path))
# logger.info(ws.transfer_3_to_2_to_1())
# logger.info(ws.transfer_1_to_2())
# 1. location code
# 2. 实验文件
# 3. material template file
w = BioyondCellWorkstation(deck=deck, address="172.16.28.102", port="502", debug_mode=False)
feeding = w.run_feeding_stage()
liquid = w.run_liquid_preparation_stage(feeding.get("feeding_materials"))
transfer = w.run_transfer_stage(liquid.get("liquid_materials"))
while True:
time.sleep(1)
# re=ws.scheduler_stop()

View File

@@ -8,8 +8,8 @@ import os
# BioyondCellWorkstation 默认配置(包含所有必需参数)
API_CONFIG = {
# API 连接配置
"api_host": os.getenv("BIOYOND_API_HOST", "http://172.16.1.143:44389"),#实机
# "api_host": os.getenv("BIOYOND_API_HOST", "http://172.16.7.149:44388"),# 仿真机
# "api_host": os.getenv("BIOYOND_API_HOST", "http://172.16.1.143:44389"),#实机
"api_host": os.getenv("BIOYOND_API_HOST", "http://172.16.11.219:44388"),# 仿真机
"api_key": os.getenv("BIOYOND_API_KEY", "8A819E5C"),
"timeout": int(os.getenv("BIOYOND_TIMEOUT", "30")),
@@ -17,7 +17,7 @@ API_CONFIG = {
"report_token": os.getenv("BIOYOND_REPORT_TOKEN", "CHANGE_ME_TOKEN"),
# HTTP 服务配置
"HTTP_host": os.getenv("BIOYOND_HTTP_HOST", "172.16.2.140"), # HTTP服务监听地址监听计算机飞连ip地址
"HTTP_host": os.getenv("BIOYOND_HTTP_HOST", "172.16.11.2"), # HTTP服务监听地址监听计算机飞连ip地址
"HTTP_port": int(os.getenv("BIOYOND_HTTP_PORT", "8080")),
"debug_mode": False,# 调试模式
}

View File

@@ -1,4 +1,3 @@
import csv
import inspect
import json
@@ -139,12 +138,11 @@ class CoinCellAssemblyWorkstation(WorkstationBase):
time.sleep(2)
if not modbus_client.client.is_socket_open():
raise ValueError('modbus tcp connection failed')
self.nodes = BaseClient.load_csv(os.path.join(os.path.dirname(__file__), 'coin_cell_assembly_a.csv'))
self.nodes = BaseClient.load_csv(os.path.join(os.path.dirname(__file__), 'coin_cell_assembly_1105.csv'))
self.client = modbus_client.register_node_list(self.nodes)
else:
print("测试模式,跳过连接")
self.nodes, self.client = None, None
""" 工站的配置 """
self.success = False
@@ -986,6 +984,31 @@ class CoinCellAssemblyWorkstation(WorkstationBase):
#self.success = True
#return self.success
def run_packaging_workflow(self, workflow_config: Dict[str, Any]) -> "CoinCellAssemblyWorkstation":
config = workflow_config or {}
qiming_params = config.get("qiming") or {}
if qiming_params:
self.qiming_coin_cell_code(**qiming_params)
if config.get("init", True):
self.func_pack_device_init()
if config.get("auto", True):
self.func_pack_device_auto()
if config.get("start", True):
self.func_pack_device_start()
packaging_config = config.get("packaging") or {}
bottle_num = packaging_config.get("bottle_num")
if bottle_num is not None:
self.func_pack_send_bottle_num(bottle_num)
allpack_params = packaging_config.get("command") or {}
if allpack_params:
self.func_allpack_cmd(**allpack_params)
return self
def fun_wuliao_test(self) -> bool:
#找到data_init中构建的2个物料盘
liaopan3 = self.deck.get_resource("\u7535\u6c60\u6599\u76d8")
@@ -1199,20 +1222,34 @@ class CoinCellAssemblyWorkstation(WorkstationBase):
"""移液枪头库存 (数量, INT16)"""
inventory, read_err = self.client.register_node_list(self.nodes).use_node('REG_DATA_TIPS_INVENTORY').read(1)
return inventory
'''
def run_coin_cell_assembly_workflow(self):
self.qiming_coin_cell_code(
fujipian_panshu=1,
fujipian_juzhendianwei=0,
gemopanshu=0,
gemo_juzhendianwei=0,
lvbodian=True,
battery_pressure_mode=True,
battery_pressure=4200,
battery_clean_ignore=False,
)
self.func_pack_device_init()
self.func_pack_device_auto()
self.func_pack_device_start()
self.func_pack_send_bottle_num(1)
self.func_allpack_cmd(elec_num = 1, elec_use_num = 1, elec_vol=50, assembly_type=7, assembly_pressure=4200, file_path="/Users/sml/work")
self.func_pack_send_finished_cmd()
self.func_pack_device_stop()
# 物料转换
return self
if __name__ == "__main__":
# 简单测试
workstation = CoinCellAssemblyWorkstation(deck=CoincellDeck(setup=True, name="coin_cell_deck"))
# workstation.qiming_coin_cell_code(fujipian_panshu=1, fujipian_juzhendianwei=2, gemopanshu=3, gemo_juzhendianwei=4, lvbodian=False, battery_pressure_mode=False, battery_pressure=4200, battery_clean_ignore=False)
# print(f"工作站创建成功: {workstation.deck.name}")
# print(f"料盘数量: {len(workstation.deck.children)}")
workstation.func_pack_device_init()
workstation.func_pack_device_auto()
workstation.func_pack_device_start()
workstation.func_pack_send_bottle_num(16)
workstation.func_allpack_cmd(elec_num=16, elec_use_num=16, elec_vol=50, assembly_type=7, assembly_pressure=4200, file_path="/Users/calvincao/Desktop/work/Uni-Lab-OS-hhm")
deck = CoincellDeck(setup=True, name="coin_cell_deck")
w = CoinCellAssemblyWorkstation(deck=deck, address="172.16.28.102", port="502", debug_mode=False)
w.run_coin_cell_assembly_workflow()

View File

@@ -821,6 +821,126 @@ bioyond_cell:
title: resource_tree_transfer参数
type: object
type: UniLabJsonCommand
auto-run_feeding_stage:
feedback: {}
goal: {}
goal_default: {}
handles:
input: []
output:
- data_key: feeding_materials
data_source: executor
data_type: resource
handler_key: feeding_materials
label: Feeding Materials
placeholder_keys: {}
result:
properties:
feeding_materials:
type: array
required:
- feeding_materials
type: object
schema:
description: ''
properties:
feedback: {}
goal:
properties: {}
required: []
type: object
result: {}
required:
- goal
title: run_feeding_stage参数
type: object
type: UniLabJsonCommand
auto-run_liquid_preparation_stage:
feedback: {}
goal: {}
goal_default: {}
handles:
input:
- data_key: feeding_materials
data_source: handle
data_type: resource
handler_key: feeding_materials
label: Feeding Materials
output:
- data_key: liquid_materials
data_source: executor
data_type: resource
handler_key: liquid_materials
label: Liquid Materials
placeholder_keys: {}
result:
properties:
feeding_materials:
type: array
liquid_materials:
type: array
required:
- liquid_materials
type: object
schema:
description: ''
properties:
feedback: {}
goal:
properties:
feeding_materials:
type: array
required: []
type: object
result: {}
required:
- goal
title: run_liquid_preparation_stage参数
type: object
type: UniLabJsonCommand
auto-run_transfer_stage:
feedback: {}
goal: {}
goal_default: {}
handles:
input:
- data_key: liquid_materials
data_source: handle
data_type: resource
handler_key: liquid_materials
label: Liquid Materials
output:
- data_key: transfer_materials
data_source: executor
data_type: resource
handler_key: transfer_materials
label: Transfer Materials
placeholder_keys: {}
result:
properties:
liquid_materials:
type: array
transfer_materials:
type: array
required:
- transfer_materials
type: object
schema:
description: ''
properties:
feedback: {}
goal:
properties:
liquid_materials:
type: array
required: []
type: object
result: {}
required:
- goal
title: run_transfer_stage参数
type: object
type: UniLabJsonCommand
auto-scheduler_continue:
feedback: {}
goal: {}
@@ -1112,9 +1232,9 @@ bioyond_cell:
device_id: String
type: python
config_info: []
description: ''
description: 配液工站
handles: []
icon: benyao2.webp
icon: ''
init_param_schema:
config:
properties:

View File

@@ -79,7 +79,7 @@ coincellassemblyworkstation_device:
elec_num: null
elec_use_num: null
elec_vol: 50
file_path: C:\Users\67484\Desktop
file_path: /Users/sml/work
handles: {}
placeholder_keys: {}
result: {}
@@ -103,7 +103,7 @@ coincellassemblyworkstation_device:
default: 50
type: integer
file_path:
default: C:\Users\67484\Desktop
default: /Users/sml/work
type: string
required:
- elec_num
@@ -332,7 +332,7 @@ coincellassemblyworkstation_device:
feedback: {}
goal: {}
goal_default:
file_path: D:\coin_cell_data
file_path: /Users/sml/work
handles: {}
placeholder_keys: {}
result: {}
@@ -343,7 +343,7 @@ coincellassemblyworkstation_device:
goal:
properties:
file_path:
default: D:\coin_cell_data
default: /Users/sml/work
type: string
required: []
type: object
@@ -477,6 +477,52 @@ coincellassemblyworkstation_device:
title: qiming_coin_cell_code参数
type: object
type: UniLabJsonCommand
auto-run_coin_cell_assembly_workflow:
feedback: {}
goal: {}
goal_default: {}
handles: {}
placeholder_keys: {}
result: {}
schema:
description: ''
properties:
feedback: {}
goal:
properties: {}
required: []
type: object
result: {}
required:
- goal
title: run_coin_cell_assembly_workflow参数
type: object
type: UniLabJsonCommand
auto-run_packaging_workflow:
feedback: {}
goal: {}
goal_default:
workflow_config: null
handles: {}
placeholder_keys: {}
result: {}
schema:
description: ''
properties:
feedback: {}
goal:
properties:
workflow_config:
type: object
required:
- workflow_config
type: object
result: {}
required:
- goal
title: run_packaging_workflow参数
type: object
type: UniLabJsonCommand
module: unilabos.devices.workstation.coin_cell_assembly.coin_cell_assembly:CoinCellAssemblyWorkstation
status_types:
data_assembly_coin_cell_num: int
@@ -500,20 +546,22 @@ coincellassemblyworkstation_device:
sys_status: str
type: python
config_info: []
description: ''
description: 扣电工站
handles: []
icon: koudian.webp
icon: ''
init_param_schema:
config:
properties:
address:
default: 172.21.32.111
default: 172.16.28.102
type: string
config:
type: object
debug_mode:
default: false
type: boolean
deck:
type: object
type: string
port:
default: '502'
type: string

View File

@@ -654,6 +654,31 @@ liquid_handler:
title: iter_tips参数
type: object
type: UniLabJsonCommand
auto-post_init:
feedback: {}
goal: {}
goal_default:
ros_node: null
handles: {}
placeholder_keys: {}
result: {}
schema:
description: ''
properties:
feedback: {}
goal:
properties:
ros_node:
type: string
required:
- ros_node
type: object
result: {}
required:
- goal
title: post_init参数
type: object
type: UniLabJsonCommand
auto-set_group:
feedback: {}
goal: {}
@@ -6170,6 +6195,31 @@ liquid_handler.prcxi:
title: move_to参数
type: object
type: UniLabJsonCommandAsync
auto-post_init:
feedback: {}
goal: {}
goal_default:
ros_node: null
handles: {}
placeholder_keys: {}
result: {}
schema:
description: ''
properties:
feedback: {}
goal:
properties:
ros_node:
type: object
required:
- ros_node
type: object
result: {}
required:
- goal
title: post_init参数
type: object
type: UniLabJsonCommand
auto-run_protocol:
feedback: {}
goal: {}

View File

@@ -1,6 +1,8 @@
neware_battery_test_system:
category:
- neware_battery_test_system
- neware
- battery_test
class:
action_value_mappings:
auto-post_init:
@@ -70,6 +72,38 @@ neware_battery_test_system:
title: test_connection参数
type: object
type: UniLabJsonCommand
debug_resource_names:
feedback: {}
goal: {}
goal_default: {}
handles: {}
result:
return_info: return_info
success: success
schema:
description: 调试方法:显示所有资源的实际名称
properties:
feedback: {}
goal:
properties: {}
required: []
type: object
result:
properties:
return_info:
description: 资源调试信息
type: string
success:
description: 是否成功
type: boolean
required:
- return_info
- success
type: object
required:
- goal
type: object
type: UniLabJsonCommand
export_status_json:
feedback: {}
goal:
@@ -219,7 +253,9 @@ neware_battery_test_system:
goal_default:
string: ''
handles: {}
result: {}
result:
return_info: return_info
success: success
schema:
description: ''
properties:
@@ -252,6 +288,56 @@ neware_battery_test_system:
title: StrSingleInput
type: object
type: StrSingleInput
submit_from_csv:
feedback: {}
goal:
csv_path: string
output_dir: string
goal_default:
csv_path: ''
output_dir: .
handles: {}
result:
return_info: return_info
submitted_count: submitted_count
success: success
schema:
description: 从CSV文件批量提交Neware测试任务
properties:
feedback: {}
goal:
properties:
csv_path:
description: 输入CSV文件的绝对路径
type: string
output_dir:
description: 输出目录用于存储XML和备份文件默认当前目录
type: string
required:
- csv_path
type: object
result:
properties:
return_info:
description: 执行结果详细信息
type: string
submitted_count:
description: 成功提交的任务数量
type: integer
success:
description: 是否成功
type: boolean
total_count:
description: CSV文件中的总行数
type: integer
required:
- return_info
- success
type: object
required:
- goal
type: object
type: UniLabJsonCommand
test_connection_action:
feedback: {}
goal: {}
@@ -284,7 +370,7 @@ neware_battery_test_system:
- goal
type: object
type: UniLabJsonCommand
module: unilabos.devices.battery.neware_battery_test_system:NewareBatteryTestSystem
module: unilabos.devices.neware_battery_test_system.neware_battery_test_system:NewareBatteryTestSystem
status_types:
channel_status: dict
connection_info: dict
@@ -294,7 +380,7 @@ neware_battery_test_system:
total_channels: int
type: python
config_info: []
description: 新威电池测试系统驱动,支持720个通道的电池测试状态监控和数据导出。通过TCP通信实现远程控制包含完整的物料管理系统,支持2盘电池状态映射和监控
description: 新威电池测试系统驱动,提供720个通道的电池测试状态监控、物料管理和CSV批量提交功能。支持TCP通信实现远程控制包含完整的物料管理系统2盘电池状态映射以及从CSV文件批量提交测试任务的能力
handles: []
icon: ''
init_param_schema:
@@ -310,13 +396,13 @@ neware_battery_test_system:
port:
type: integer
size_x:
default: 500.0
default: 50
type: number
size_y:
default: 500.0
default: 50
type: number
size_z:
default: 2000.0
default: 20
type: number
timeout:
type: integer

View File

@@ -45,6 +45,31 @@ virtual_centrifuge:
title: initialize参数
type: object
type: UniLabJsonCommandAsync
auto-post_init:
feedback: {}
goal: {}
goal_default:
ros_node: null
handles: {}
placeholder_keys: {}
result: {}
schema:
description: ''
properties:
feedback: {}
goal:
properties:
ros_node:
type: object
required:
- ros_node
type: object
result: {}
required:
- goal
title: post_init参数
type: object
type: UniLabJsonCommand
centrifuge:
feedback:
current_speed: current_speed
@@ -335,6 +360,31 @@ virtual_column:
title: initialize参数
type: object
type: UniLabJsonCommandAsync
auto-post_init:
feedback: {}
goal: {}
goal_default:
ros_node: null
handles: {}
placeholder_keys: {}
result: {}
schema:
description: ''
properties:
feedback: {}
goal:
properties:
ros_node:
type: object
required:
- ros_node
type: object
result: {}
required:
- goal
title: post_init参数
type: object
type: UniLabJsonCommand
run_column:
feedback:
current_status: current_status
@@ -732,6 +782,31 @@ virtual_filter:
title: initialize参数
type: object
type: UniLabJsonCommandAsync
auto-post_init:
feedback: {}
goal: {}
goal_default:
ros_node: null
handles: {}
placeholder_keys: {}
result: {}
schema:
description: ''
properties:
feedback: {}
goal:
properties:
ros_node:
type: object
required:
- ros_node
type: object
result: {}
required:
- goal
title: post_init参数
type: object
type: UniLabJsonCommand
filter:
feedback:
current_status: current_status
@@ -1358,6 +1433,31 @@ virtual_heatchill:
title: initialize参数
type: object
type: UniLabJsonCommandAsync
auto-post_init:
feedback: {}
goal: {}
goal_default:
ros_node: null
handles: {}
placeholder_keys: {}
result: {}
schema:
description: ''
properties:
feedback: {}
goal:
properties:
ros_node:
type: object
required:
- ros_node
type: object
result: {}
required:
- goal
title: post_init参数
type: object
type: UniLabJsonCommand
heat_chill:
feedback:
status: status
@@ -2358,6 +2458,31 @@ virtual_rotavap:
title: initialize参数
type: object
type: UniLabJsonCommandAsync
auto-post_init:
feedback: {}
goal: {}
goal_default:
ros_node: null
handles: {}
placeholder_keys: {}
result: {}
schema:
description: ''
properties:
feedback: {}
goal:
properties:
ros_node:
type: object
required:
- ros_node
type: object
result: {}
required:
- goal
title: post_init参数
type: object
type: UniLabJsonCommand
evaporate:
feedback:
current_device: current_device
@@ -2690,6 +2815,31 @@ virtual_separator:
title: initialize参数
type: object
type: UniLabJsonCommandAsync
auto-post_init:
feedback: {}
goal: {}
goal_default:
ros_node: null
handles: {}
placeholder_keys: {}
result: {}
schema:
description: ''
properties:
feedback: {}
goal:
properties:
ros_node:
type: object
required:
- ros_node
type: object
result: {}
required:
- goal
title: post_init参数
type: object
type: UniLabJsonCommand
separate:
feedback:
current_status: status
@@ -3600,6 +3750,31 @@ virtual_solenoid_valve:
title: is_closed参数
type: object
type: UniLabJsonCommand
auto-post_init:
feedback: {}
goal: {}
goal_default:
ros_node: null
handles: {}
placeholder_keys: {}
result: {}
schema:
description: ''
properties:
feedback: {}
goal:
properties:
ros_node:
type: object
required:
- ros_node
type: object
result: {}
required:
- goal
title: post_init参数
type: object
type: UniLabJsonCommand
auto-reset:
feedback: {}
goal: {}
@@ -4177,6 +4352,31 @@ virtual_solid_dispenser:
title: parse_mol_string参数
type: object
type: UniLabJsonCommand
auto-post_init:
feedback: {}
goal: {}
goal_default:
ros_node: null
handles: {}
placeholder_keys: {}
result: {}
schema:
description: ''
properties:
feedback: {}
goal:
properties:
ros_node:
type: object
required:
- ros_node
type: object
result: {}
required:
- goal
title: post_init参数
type: object
type: UniLabJsonCommand
module: unilabos.devices.virtual.virtual_solid_dispenser:VirtualSolidDispenser
status_types:
current_reagent: str
@@ -4278,6 +4478,31 @@ virtual_stirrer:
title: initialize参数
type: object
type: UniLabJsonCommandAsync
auto-post_init:
feedback: {}
goal: {}
goal_default:
ros_node: null
handles: {}
placeholder_keys: {}
result: {}
schema:
description: ''
properties:
feedback: {}
goal:
properties:
ros_node:
type: object
required:
- ros_node
type: object
result: {}
required:
- goal
title: post_init参数
type: object
type: UniLabJsonCommand
start_stir:
feedback:
status: status
@@ -4995,6 +5220,31 @@ virtual_transfer_pump:
title: is_full参数
type: object
type: UniLabJsonCommand
auto-post_init:
feedback: {}
goal: {}
goal_default:
ros_node: null
handles: {}
placeholder_keys: {}
result: {}
schema:
description: ''
properties:
feedback: {}
goal:
properties:
ros_node:
type: object
required:
- ros_node
type: object
result: {}
required:
- goal
title: post_init参数
type: object
type: UniLabJsonCommand
auto-pull_plunger:
feedback: {}
goal: {}

View File

@@ -1,16 +1,3 @@
YB_qiang_tou:
category:
- yb3
- YB_bottle
class:
module: unilabos.resources.bioyond.YB_bottles:YB_qiang_tou
type: pylabrobot
description: YB_qiang_tou
handles: []
icon: ''
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_20ml_fenyeping:
category:
- yb3
@@ -37,6 +24,19 @@ YB_5ml_fenyeping:
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_jia_yang_tou_da:
category:
- yb3
- YB_bottle
class:
module: unilabos.resources.bioyond.YB_bottles:YB_jia_yang_tou_da
type: pylabrobot
description: YB_jia_yang_tou_da
handles: []
icon: ''
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_pei_ye_da_Bottle:
category:
- yb3
@@ -63,14 +63,14 @@ YB_pei_ye_xiao_Bottle:
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_jia_yang_tou_da:
YB_qiang_tou:
category:
- yb3
- YB_bottle
class:
module: unilabos.resources.bioyond.YB_bottles:YB_jia_yang_tou_da
module: unilabos.resources.bioyond.YB_bottles:YB_qiang_tou
type: pylabrobot
description: YB_jia_yang_tou_da
description: YB_qiang_tou
handles: []
icon: ''
init_param_schema: {}
@@ -80,6 +80,7 @@ YB_ye_Bottle:
category:
- yb3
- YB_bottle_carriers
- YB_bottle
class:
module: unilabos.resources.bioyond.YB_bottles:YB_ye_Bottle
type: pylabrobot
@@ -88,4 +89,4 @@ YB_ye_Bottle:
icon: ''
init_param_schema: {}
registry_type: resource
version: 1.0.0
version: 1.0.0

View File

@@ -11,71 +11,6 @@ YB_100ml_yeti:
init_param_schema: {}
registry_type: resource
version: 1.0.0
# YB_1BottleCarrier:
# category:
# - yb3
# - YB_bottle_carriers
# class:
# module: unilabos.resources.bioyond.YB_bottle_carriers:YB_1BottleCarrier
# type: pylabrobot
# description: YB_1BottleCarrier
# handles: []
# icon: ''
# init_param_schema: {}
# registry_type: resource
# version: 1.0.0
YB_gaonianye:
category:
- yb3
- YB_bottle_carriers
class:
module: unilabos.resources.bioyond.YB_bottle_carriers:YB_gaonianye
type: pylabrobot
description: YB_gaonianye
handles: []
icon: ''
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_peiyepingdaban:
category:
- yb3
- YB_bottle_carriers
class:
module: unilabos.resources.bioyond.YB_bottle_carriers:YB_peiyepingdaban
type: pylabrobot
description: YB_peiyepingdaban
handles: []
icon: ''
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_6StockCarrier:
category:
- yb3
- YB_bottle_carriers
class:
module: unilabos.resources.bioyond.YB_bottle_carriers:YB_6StockCarrier
type: pylabrobot
description: YB_6StockCarrier
handles: []
icon: ''
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_6VialCarrier:
category:
- yb3
- YB_bottle_carriers
class:
module: unilabos.resources.bioyond.YB_bottle_carriers:YB_6VialCarrier
type: pylabrobot
description: YB_6VialCarrier
handles: []
icon: ''
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_20ml_fenyepingban:
category:
- yb3
@@ -102,40 +37,27 @@ YB_5ml_fenyepingban:
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_peiyepingxiaoban:
YB_6StockCarrier:
category:
- yb3
- YB_bottle_carriers
class:
module: unilabos.resources.bioyond.YB_bottle_carriers:YB_peiyepingxiaoban
module: unilabos.resources.bioyond.YB_bottle_carriers:YB_6StockCarrier
type: pylabrobot
description: YB_peiyepingxiaoban
description: YB_6StockCarrier
handles: []
icon: ''
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_shi_pei_qi_kuai:
YB_6VialCarrier:
category:
- yb3
- YB_bottle_carriers
class:
module: unilabos.resources.bioyond.YB_bottle_carriers:YB_shi_pei_qi_kuai
module: unilabos.resources.bioyond.YB_bottle_carriers:YB_6VialCarrier
type: pylabrobot
description: YB_shi_pei_qi_kuai
handles: []
icon: ''
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_qiang_tou_he:
category:
- yb3
- YB_bottle_carriers
class:
module: unilabos.resources.bioyond.YB_bottle_carriers:YB_qiang_tou_he
type: pylabrobot
description: YB_qiang_tou_he
description: YB_6VialCarrier
handles: []
icon: ''
init_param_schema: {}
@@ -154,19 +76,19 @@ YB_gao_nian_ye_Bottle:
init_param_schema: {}
registry_type: resource
version: 1.0.0
# YB_jia_yang_tou_da:
# category:
# - yb3
# - YB_bottle_carriers
# class:
# module: unilabos.resources.bioyond.YB_bottles:YB_jia_yang_tou_da
# type: pylabrobot
# description: YB_jia_yang_tou_da
# handles: []
# icon: ''
# init_param_schema: {}
# registry_type: resource
# version: 1.0.0
YB_gaonianye:
category:
- yb3
- YB_bottle_carriers
class:
module: unilabos.resources.bioyond.YB_bottle_carriers:YB_gaonianye
type: pylabrobot
description: YB_gaonianye
handles: []
icon: ''
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_jia_yang_tou_da_Carrier:
category:
- yb3
@@ -180,14 +102,53 @@ YB_jia_yang_tou_da_Carrier:
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_ye_100ml_Bottle:
YB_peiyepingdaban:
category:
- yb3
- YB_bottle_carriers
class:
module: unilabos.resources.bioyond.YB_bottles:YB_ye_100ml_Bottle
module: unilabos.resources.bioyond.YB_bottle_carriers:YB_peiyepingdaban
type: pylabrobot
description: YB_ye_100ml_Bottle
description: YB_peiyepingdaban
handles: []
icon: ''
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_peiyepingxiaoban:
category:
- yb3
- YB_bottle_carriers
class:
module: unilabos.resources.bioyond.YB_bottle_carriers:YB_peiyepingxiaoban
type: pylabrobot
description: YB_peiyepingxiaoban
handles: []
icon: ''
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_qiang_tou_he:
category:
- yb3
- YB_bottle_carriers
class:
module: unilabos.resources.bioyond.YB_bottle_carriers:YB_qiang_tou_he
type: pylabrobot
description: YB_qiang_tou_he
handles: []
icon: ''
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_shi_pei_qi_kuai:
category:
- yb3
- YB_bottle_carriers
class:
module: unilabos.resources.bioyond.YB_bottle_carriers:YB_shi_pei_qi_kuai
type: pylabrobot
description: YB_shi_pei_qi_kuai
handles: []
icon: ''
init_param_schema: {}
@@ -206,3 +167,16 @@ YB_ye:
init_param_schema: {}
registry_type: resource
version: 1.0.0
YB_ye_100ml_Bottle:
category:
- yb3
- YB_bottle_carriers
class:
module: unilabos.resources.bioyond.YB_bottles:YB_ye_100ml_Bottle
type: pylabrobot
description: YB_ye_100ml_Bottle
handles: []
icon: ''
init_param_schema: {}
registry_type: resource
version: 1.0.0