mirror of
https://github.com/deepmodeling/Uni-Lab-OS
synced 2026-04-27 12:41:21 +00:00
fix(neware): 修复 submit_auto_export_excel 因 resource=[] 导致 0 下发 + filetype kwarg
问题: - 日志中 submit_auto_export_excel 收到 resource=[](工作流本身不传成品电池资源, 电池由人工搬运),原代码 n = len(resource) = 0 → 整个循环跳过 → "共 0 颗电池,成功下发 0 颗"。 - neware_driver.start_test 原来不接收 filetype kwarg,导致 TypeError 阻塞下发。 修复: 1. submit_auto_export_excel 改为由 mount_resource 驱动循环长度: - 新签名以 mount_resource 为主,resource/pole_weight/coin_cell_code 均可选 - 新增 coin_cell_code 入参,coin_id 优先级 coin_cell_code > resource.name > fallback - n==0 时提前返回并给出明确错误信息 2. manual_confirm 的返回值与 YAML handles/output 新增 coin_cell_code (从已解包的 assembly_data 直接取) 3. submit_auto_export_excel YAML goal/schema/goal_default/handles.input 新增 coin_cell_code;required 中移除 resource(不再强制) 4. neware_driver.build_start_command / start_test 增加 filetype:int=1 参数, 动态嵌入 XML backup 配置,消除 TypeError Made-with: Cursor
This commit is contained in:
@@ -1934,6 +1934,55 @@ class NewareBatteryTestSystem:
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def mock_assembly_data(self) -> dict:
|
||||||
|
"""
|
||||||
|
模拟扣电组装站 auto-func_sendbottle_allpack_multi 的输出,返回固定的 2 颗电池 assembly_data。
|
||||||
|
用于在没有真实扣电组装站的情况下,测试
|
||||||
|
mock_assembly_data → manual_confirm → battery_transfer_confirm → submit_auto_export_excel
|
||||||
|
的完整参数传递与 TCP 下发链路。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: {
|
||||||
|
"assembly_data": list[dict], # 9 字段 × 2 颗电池
|
||||||
|
"success": bool,
|
||||||
|
"return_info": str,
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
assembly_data = [
|
||||||
|
{
|
||||||
|
"Time": "20260421_143022",
|
||||||
|
"open_circuit_voltage": 3.721,
|
||||||
|
"pole_weight": 98.43,
|
||||||
|
"assembly_time": 120,
|
||||||
|
"assembly_pressure": 5.2,
|
||||||
|
"electrolyte_volume": 80.0,
|
||||||
|
"data_coin_type": 2,
|
||||||
|
"electrolyte_code": "EL-2026042101",
|
||||||
|
"coin_cell_code": "CC-2026042101",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Time": "20260421_143255",
|
||||||
|
"open_circuit_voltage": 3.698,
|
||||||
|
"pole_weight": 97.85,
|
||||||
|
"assembly_time": 118,
|
||||||
|
"assembly_pressure": 5.1,
|
||||||
|
"electrolyte_volume": 79.5,
|
||||||
|
"data_coin_type": 2,
|
||||||
|
"electrolyte_code": "EL-2026042102",
|
||||||
|
"coin_cell_code": "CC-2026042102",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
info = f"mock_assembly_data 返回 {len(assembly_data)} 颗电池的模拟组装数据"
|
||||||
|
if self._ros_node:
|
||||||
|
self._ros_node.lab_logger().info(f"[mock_assembly_data] {info}")
|
||||||
|
else:
|
||||||
|
print(f"[mock_assembly_data] {info}")
|
||||||
|
return {
|
||||||
|
"assembly_data": assembly_data,
|
||||||
|
"success": True,
|
||||||
|
"return_info": info,
|
||||||
|
}
|
||||||
|
|
||||||
def manual_confirm(
|
def manual_confirm(
|
||||||
self,
|
self,
|
||||||
resource: List[ResourceSlot],
|
resource: List[ResourceSlot],
|
||||||
@@ -2013,6 +2062,7 @@ class NewareBatteryTestSystem:
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"resource": resource_dump,
|
"resource": resource_dump,
|
||||||
|
"coin_cell_code": coin_cell_code,
|
||||||
"target_device": target_device,
|
"target_device": target_device,
|
||||||
"mount_resource": mount_resource_dump,
|
"mount_resource": mount_resource_dump,
|
||||||
"collector_mass": collector_mass,
|
"collector_mass": collector_mass,
|
||||||
@@ -2202,25 +2252,29 @@ class NewareBatteryTestSystem:
|
|||||||
|
|
||||||
async def submit_auto_export_excel(
|
async def submit_auto_export_excel(
|
||||||
self,
|
self,
|
||||||
resource: List[ResourceSlot],
|
|
||||||
mount_resource: List[ResourceSlot],
|
mount_resource: List[ResourceSlot],
|
||||||
collector_mass: List[float],
|
collector_mass: List[float],
|
||||||
active_material: List[float],
|
active_material: List[float],
|
||||||
capacity: List[float],
|
capacity: List[float],
|
||||||
battery_system: List[str],
|
battery_system: List[str],
|
||||||
pole_weight: List[float] = None,
|
pole_weight: List[float] = None,
|
||||||
|
coin_cell_code: List[str] = None,
|
||||||
|
resource: List[ResourceSlot] = None,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
对每颗电池计算测试参数、生成 XML 工步文件并通过 TCP 下发给新威测试仪。
|
对每颗电池计算测试参数、生成 XML 工步文件并通过 TCP 下发给新威测试仪。
|
||||||
|
|
||||||
|
循环长度由 mount_resource 驱动(真正要下发的通道数量)。
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
resource: 成品电池资源列表(含 pole_weight 状态,仅当 pole_weight 入参为空时作为回退)
|
mount_resource: 目标通道资源列表(含 Channel_Name = devid-subdevid-chlid),循环长度来源
|
||||||
mount_resource: 目标通道资源列表(含 Channel_Name = devid-subdevid-chlid)
|
|
||||||
collector_mass: 各电池集流体质量 (mg)
|
collector_mass: 各电池集流体质量 (mg)
|
||||||
active_material: 各电池活性物质比例(0.97 或 "97%")
|
active_material: 各电池活性物质比例(0.97 或 "97%")
|
||||||
capacity: 各电池克容量 (mAh/g)
|
capacity: 各电池克容量 (mAh/g)
|
||||||
battery_system: xml 工步标识(如 "811_LI_002")
|
battery_system: xml 工步标识(如 "811_LI_002")
|
||||||
pole_weight: 各电池极片质量 (mg),来自上游 manual_confirm 的透传;为 None 时回退到从 resource 状态提取
|
pole_weight: 各电池极片质量 (mg),来自上游 manual_confirm 的透传;为空时回退到从 resource 状态提取
|
||||||
|
coin_cell_code: 各电池条码(来自上游 manual_confirm 从 assembly_data 解包);作为 Neware 备份文件的 CoinID/barcode
|
||||||
|
resource: 成品电池资源列表(可选);仅在 coin_cell_code 与 pole_weight 均未提供时作为回退
|
||||||
"""
|
"""
|
||||||
import importlib
|
import importlib
|
||||||
gen_mod = importlib.import_module(
|
gen_mod = importlib.import_module(
|
||||||
@@ -2228,10 +2282,26 @@ class NewareBatteryTestSystem:
|
|||||||
)
|
)
|
||||||
from .neware_driver import start_test as _start_test
|
from .neware_driver import start_test as _start_test
|
||||||
|
|
||||||
n = len(resource)
|
resource = resource or []
|
||||||
|
pole_weight = pole_weight or []
|
||||||
|
coin_cell_code = coin_cell_code or []
|
||||||
|
|
||||||
|
n = len(mount_resource) if mount_resource else 0
|
||||||
results = []
|
results = []
|
||||||
submitted = 0
|
submitted = 0
|
||||||
|
|
||||||
|
if n == 0:
|
||||||
|
msg = "mount_resource 为空,没有通道可下发"
|
||||||
|
if self._ros_node:
|
||||||
|
self._ros_node.lab_logger().warning(f"[test] {msg}")
|
||||||
|
return {
|
||||||
|
"return_info": f"共 0 颗电池,成功下发 0 颗({msg})",
|
||||||
|
"success": False,
|
||||||
|
"submitted_count": 0,
|
||||||
|
"total_count": 0,
|
||||||
|
"results": [],
|
||||||
|
}
|
||||||
|
|
||||||
xml_dir = os.path.join(os.path.dirname(__file__), "xml_recipes")
|
xml_dir = os.path.join(os.path.dirname(__file__), "xml_recipes")
|
||||||
os.makedirs(xml_dir, exist_ok=True)
|
os.makedirs(xml_dir, exist_ok=True)
|
||||||
backup_dir = self._last_backup_dir or os.path.join(os.path.dirname(__file__), "backup")
|
backup_dir = self._last_backup_dir or os.path.join(os.path.dirname(__file__), "backup")
|
||||||
@@ -2248,17 +2318,20 @@ class NewareBatteryTestSystem:
|
|||||||
raise ValueError(f"Channel_Name 格式错误,期望 devid-subdevid-chlid,实际: {ch_name}")
|
raise ValueError(f"Channel_Name 格式错误,期望 devid-subdevid-chlid,实际: {ch_name}")
|
||||||
devid, subdevid, chlid = int(parts[0]), int(parts[1]), int(parts[2])
|
devid, subdevid, chlid = int(parts[0]), int(parts[1]), int(parts[2])
|
||||||
|
|
||||||
# 2. 获取电池标识与极片重量
|
# 2. 获取电池标识与极片重量(按优先级 coin_cell_code > resource > 兜底)
|
||||||
res = resource[i]
|
res = resource[i] if i < len(resource) else None
|
||||||
coin_id = (
|
coin_id = (
|
||||||
getattr(res, "name", None)
|
(coin_cell_code[i] if i < len(coin_cell_code) and coin_cell_code[i] else None)
|
||||||
|
or (getattr(res, "name", None) if res is not None else None)
|
||||||
or (res.get("name") if isinstance(res, dict) else None)
|
or (res.get("name") if isinstance(res, dict) else None)
|
||||||
or f"battery_{i}"
|
or f"battery_{i}"
|
||||||
)
|
)
|
||||||
if pole_weight and i < len(pole_weight):
|
if pole_weight and i < len(pole_weight):
|
||||||
pw = float(pole_weight[i])
|
pw = float(pole_weight[i])
|
||||||
else:
|
elif res is not None:
|
||||||
pw = self._extract_pole_weight(res)
|
pw = self._extract_pole_weight(res)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"无法获取 pole_weight:pole_weight 列表长度不足 且 resource 为空")
|
||||||
|
|
||||||
# 3. 计算活性物质质量与容量
|
# 3. 计算活性物质质量与容量
|
||||||
cm = float(collector_mass[i])
|
cm = float(collector_mass[i])
|
||||||
|
|||||||
@@ -5,14 +5,18 @@ def build_start_command(devid, subdevid, chlid, CoinID,
|
|||||||
ip_in_xml="127.0.0.1",
|
ip_in_xml="127.0.0.1",
|
||||||
devtype:int=27,
|
devtype:int=27,
|
||||||
recipe_path:str=f"D:\\HHM_test\\A001.xml",
|
recipe_path:str=f"D:\\HHM_test\\A001.xml",
|
||||||
backup_dir:str=f"D:\\HHM_test\\backup") -> str:
|
backup_dir:str=f"D:\\HHM_test\\backup",
|
||||||
|
filetype:int=1) -> str:
|
||||||
|
"""
|
||||||
|
filetype: 备份文件类型。0=NDA(新威原生),1=Excel。默认 1。
|
||||||
|
"""
|
||||||
lines = [
|
lines = [
|
||||||
'<?xml version="1.0" encoding="UTF-8"?>',
|
'<?xml version="1.0" encoding="UTF-8"?>',
|
||||||
'<bts version="1.0">',
|
'<bts version="1.0">',
|
||||||
' <cmd>start</cmd>',
|
' <cmd>start</cmd>',
|
||||||
' <list count="1">',
|
' <list count="1">',
|
||||||
f' <start ip="{ip_in_xml}" devtype="{devtype}" devid="{devid}" subdevid="{subdevid}" chlid="{chlid}" barcode="{CoinID}">{recipe_path}</start>',
|
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="1" backupontime="1" backupontimeinterval="1" backupfree="0" />',
|
f' <backup backupdir="{backup_dir}" remotedir="" filenametype="1" customfilename="" createdirbydate="0" filetype="{int(filetype)}" backupontime="1" backupontimeinterval="1" backupfree="0" />',
|
||||||
' </list>',
|
' </list>',
|
||||||
'</bts>',
|
'</bts>',
|
||||||
]
|
]
|
||||||
@@ -36,8 +40,11 @@ def recv_until_marks(sock: socket.socket, timeout=60):
|
|||||||
return bytes(buf)
|
return bytes(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"):
|
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", filetype:int=1):
|
||||||
xml_cmd = build_start_command(devid=devid, subdevid=subdevid, chlid=chlid, CoinID=CoinID, recipe_path=recipe_path, backup_dir=backup_dir)
|
"""
|
||||||
|
filetype: 备份文件类型,0=NDA,1=Excel。默认 1。
|
||||||
|
"""
|
||||||
|
xml_cmd = build_start_command(devid=devid, subdevid=subdevid, chlid=chlid, CoinID=CoinID, recipe_path=recipe_path, backup_dir=backup_dir, filetype=filetype)
|
||||||
#print(xml_cmd)
|
#print(xml_cmd)
|
||||||
with socket.create_connection((ip, port), timeout=60) as s:
|
with socket.create_connection((ip, port), timeout=60) as s:
|
||||||
s.sendall(xml_cmd.encode("utf-8"))
|
s.sendall(xml_cmd.encode("utf-8"))
|
||||||
|
|||||||
@@ -354,6 +354,42 @@ neware_battery_test_system:
|
|||||||
title: upload_backup_to_oss参数
|
title: upload_backup_to_oss参数
|
||||||
type: object
|
type: object
|
||||||
type: UniLabJsonCommand
|
type: UniLabJsonCommand
|
||||||
|
mock_assembly_data:
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
goal: {}
|
||||||
|
feedback: {}
|
||||||
|
result:
|
||||||
|
assembly_data: assembly_data
|
||||||
|
success: success
|
||||||
|
return_info: return_info
|
||||||
|
schema:
|
||||||
|
title: mock_assembly_data参数
|
||||||
|
description: 模拟扣电组装站 auto-func_sendbottle_allpack_multi 输出固定的 assembly_data(用于测试 neware 完整链路)
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
goal:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
unilabos_device_id:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: UniLabOS设备ID,用于指定执行动作的具体设备实例
|
||||||
|
required: []
|
||||||
|
feedback: {}
|
||||||
|
result:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
goal_default: {}
|
||||||
|
handles:
|
||||||
|
input: []
|
||||||
|
output:
|
||||||
|
- handler_key: assembly_data_output
|
||||||
|
data_type: array
|
||||||
|
label: 扣电组装数据列表(模拟)
|
||||||
|
data_key: assembly_data
|
||||||
|
data_source: executor
|
||||||
|
placeholder_keys: {}
|
||||||
manual_confirm:
|
manual_confirm:
|
||||||
type: UniLabJsonCommand
|
type: UniLabJsonCommand
|
||||||
goal:
|
goal:
|
||||||
@@ -372,6 +408,7 @@ neware_battery_test_system:
|
|||||||
feedback: {}
|
feedback: {}
|
||||||
result:
|
result:
|
||||||
resource: resource
|
resource: resource
|
||||||
|
coin_cell_code: coin_cell_code
|
||||||
target_device: target_device
|
target_device: target_device
|
||||||
mount_resource: mount_resource
|
mount_resource: mount_resource
|
||||||
collector_mass: collector_mass
|
collector_mass: collector_mass
|
||||||
@@ -711,6 +748,11 @@ neware_battery_test_system:
|
|||||||
label: 极片质量
|
label: 极片质量
|
||||||
data_key: pole_weight
|
data_key: pole_weight
|
||||||
data_source: executor
|
data_source: executor
|
||||||
|
- handler_key: coin_cell_code
|
||||||
|
data_type: array
|
||||||
|
label: 电池条码列表
|
||||||
|
data_key: coin_cell_code
|
||||||
|
data_source: executor
|
||||||
placeholder_keys:
|
placeholder_keys:
|
||||||
resource: unilabos_resources
|
resource: unilabos_resources
|
||||||
target_device: unilabos_devices
|
target_device: unilabos_devices
|
||||||
@@ -729,6 +771,7 @@ neware_battery_test_system:
|
|||||||
capacity: capacity
|
capacity: capacity
|
||||||
battery_system: battery_system
|
battery_system: battery_system
|
||||||
pole_weight: pole_weight
|
pole_weight: pole_weight
|
||||||
|
coin_cell_code: coin_cell_code
|
||||||
feedback: {}
|
feedback: {}
|
||||||
result:
|
result:
|
||||||
return_info: return_info
|
return_info: return_info
|
||||||
@@ -932,8 +975,11 @@ neware_battery_test_system:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: number
|
type: number
|
||||||
|
coin_cell_code:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
required:
|
required:
|
||||||
- resource
|
|
||||||
- mount_resource
|
- mount_resource
|
||||||
- collector_mass
|
- collector_mass
|
||||||
- active_material
|
- active_material
|
||||||
@@ -954,6 +1000,7 @@ neware_battery_test_system:
|
|||||||
capacity: []
|
capacity: []
|
||||||
battery_system: []
|
battery_system: []
|
||||||
pole_weight: []
|
pole_weight: []
|
||||||
|
coin_cell_code: []
|
||||||
handles:
|
handles:
|
||||||
input:
|
input:
|
||||||
- handler_key: resource
|
- handler_key: resource
|
||||||
@@ -998,6 +1045,12 @@ neware_battery_test_system:
|
|||||||
data_key: pole_weight
|
data_key: pole_weight
|
||||||
data_source: handle
|
data_source: handle
|
||||||
io_type: source
|
io_type: source
|
||||||
|
- handler_key: coin_cell_code
|
||||||
|
data_type: array
|
||||||
|
label: 电池条码列表
|
||||||
|
data_key: coin_cell_code
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
output: []
|
output: []
|
||||||
placeholder_keys:
|
placeholder_keys:
|
||||||
resource: unilabos_resources
|
resource: unilabos_resources
|
||||||
|
|||||||
Reference in New Issue
Block a user