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:
Xie Qiming
2026-04-22 16:24:35 +08:00
parent f431d61d85
commit 79c0815b70
3 changed files with 147 additions and 14 deletions

View File

@@ -1934,6 +1934,55 @@ class NewareBatteryTestSystem:
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(
self,
resource: List[ResourceSlot],
@@ -2013,6 +2062,7 @@ class NewareBatteryTestSystem:
return {
"resource": resource_dump,
"coin_cell_code": coin_cell_code,
"target_device": target_device,
"mount_resource": mount_resource_dump,
"collector_mass": collector_mass,
@@ -2202,25 +2252,29 @@ class NewareBatteryTestSystem:
async def submit_auto_export_excel(
self,
resource: List[ResourceSlot],
mount_resource: List[ResourceSlot],
collector_mass: List[float],
active_material: List[float],
capacity: List[float],
battery_system: List[str],
pole_weight: List[float] = None,
coin_cell_code: List[str] = None,
resource: List[ResourceSlot] = None,
) -> dict:
"""
对每颗电池计算测试参数、生成 XML 工步文件并通过 TCP 下发给新威测试仪。
循环长度由 mount_resource 驱动(真正要下发的通道数量)。
Args:
resource: 成品电池资源列表(含 pole_weight 状态,仅当 pole_weight 入参为空时作为回退)
mount_resource: 目标通道资源列表(含 Channel_Name = devid-subdevid-chlid
mount_resource: 目标通道资源列表(含 Channel_Name = devid-subdevid-chlid循环长度来源
collector_mass: 各电池集流体质量 (mg)
active_material: 各电池活性物质比例0.97 或 "97%"
capacity: 各电池克容量 (mAh/g)
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
gen_mod = importlib.import_module(
@@ -2228,10 +2282,26 @@ class NewareBatteryTestSystem:
)
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 = []
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")
os.makedirs(xml_dir, exist_ok=True)
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}")
devid, subdevid, chlid = int(parts[0]), int(parts[1]), int(parts[2])
# 2. 获取电池标识与极片重量
res = resource[i]
# 2. 获取电池标识与极片重量(按优先级 coin_cell_code > resource > 兜底)
res = resource[i] if i < len(resource) else None
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 f"battery_{i}"
)
if pole_weight and i < len(pole_weight):
pw = float(pole_weight[i])
else:
elif res is not None:
pw = self._extract_pole_weight(res)
else:
raise ValueError(f"无法获取 pole_weightpole_weight 列表长度不足 且 resource 为空")
# 3. 计算活性物质质量与容量
cm = float(collector_mass[i])

View File

@@ -5,14 +5,18 @@ 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:
backup_dir:str=f"D:\\HHM_test\\backup",
filetype:int=1) -> str:
"""
filetype: 备份文件类型。0=NDA新威原生1=Excel。默认 1。
"""
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="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>',
'</bts>',
]
@@ -36,8 +40,11 @@ def recv_until_marks(sock: socket.socket, timeout=60):
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)
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):
"""
filetype: 备份文件类型0=NDA1=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)
with socket.create_connection((ip, port), timeout=60) as s:
s.sendall(xml_cmd.encode("utf-8"))

View File

@@ -354,6 +354,42 @@ neware_battery_test_system:
title: upload_backup_to_oss参数
type: object
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:
type: UniLabJsonCommand
goal:
@@ -372,6 +408,7 @@ neware_battery_test_system:
feedback: {}
result:
resource: resource
coin_cell_code: coin_cell_code
target_device: target_device
mount_resource: mount_resource
collector_mass: collector_mass
@@ -711,6 +748,11 @@ neware_battery_test_system:
label: 极片质量
data_key: pole_weight
data_source: executor
- handler_key: coin_cell_code
data_type: array
label: 电池条码列表
data_key: coin_cell_code
data_source: executor
placeholder_keys:
resource: unilabos_resources
target_device: unilabos_devices
@@ -729,6 +771,7 @@ neware_battery_test_system:
capacity: capacity
battery_system: battery_system
pole_weight: pole_weight
coin_cell_code: coin_cell_code
feedback: {}
result:
return_info: return_info
@@ -932,8 +975,11 @@ neware_battery_test_system:
type: array
items:
type: number
coin_cell_code:
type: array
items:
type: string
required:
- resource
- mount_resource
- collector_mass
- active_material
@@ -954,6 +1000,7 @@ neware_battery_test_system:
capacity: []
battery_system: []
pole_weight: []
coin_cell_code: []
handles:
input:
- handler_key: resource
@@ -998,6 +1045,12 @@ neware_battery_test_system:
data_key: pole_weight
data_source: handle
io_type: source
- handler_key: coin_cell_code
data_type: array
label: 电池条码列表
data_key: coin_cell_code
data_source: handle
io_type: source
output: []
placeholder_keys:
resource: unilabos_resources