mirror of
https://github.com/deepmodeling/Uni-Lab-OS
synced 2026-04-29 03:34:30 +00:00
Update neware battery test system driver and registry
- Expand neware_battery_test_system.py with new actions and logic - Update generate_xml_content.py with additional XML generation support - Extend neware_battery_test_system.yaml registry with new action schemas - Update OSS upload READMEs and device.json - Add electrode_sheet.py resource fields Made-with: Cursor
This commit is contained in:
@@ -219,10 +219,10 @@ device = NewareBatteryTestSystem(
|
|||||||
|
|
||||||
#### 步骤 2:提交测试任务
|
#### 步骤 2:提交测试任务
|
||||||
|
|
||||||
使用 `submit_from_csv` 提交测试任务:
|
使用 `submit_from_csv_export_ndax` 提交测试任务:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
result = device.submit_from_csv(
|
result = device.submit_from_csv_export_ndax(
|
||||||
csv_path="test_data.csv",
|
csv_path="test_data.csv",
|
||||||
output_dir="D:/neware_output"
|
output_dir="D:/neware_output"
|
||||||
)
|
)
|
||||||
@@ -489,7 +489,7 @@ A: 重新获取新的 Token 并更新环境变量 `UNI_LAB_AUTH_TOKEN`。
|
|||||||
**Q: 可以自定义上传路径吗?**
|
**Q: 可以自定义上传路径吗?**
|
||||||
A: 当前版本路径由统一 API 自动分配,`oss_prefix` 参数暂不使用(保留接口兼容性)。
|
A: 当前版本路径由统一 API 自动分配,`oss_prefix` 参数暂不使用(保留接口兼容性)。
|
||||||
|
|
||||||
**Q: 为什么不在 `submit_from_csv` 中自动上传?**
|
**Q: 为什么不在 `submit_from_csv_export_ndax` 中自动上传?**
|
||||||
A: 因为备份文件在测试进行中逐步生成,方法返回时可能文件尚未完全生成,因此提供独立的上传方法更灵活。
|
A: 因为备份文件在测试进行中逐步生成,方法返回时可能文件尚未完全生成,因此提供独立的上传方法更灵活。
|
||||||
|
|
||||||
**Q: 上传后如何访问文件?**
|
**Q: 上传后如何访问文件?**
|
||||||
|
|||||||
@@ -230,10 +230,10 @@ device = NewareBatteryTestSystem(
|
|||||||
|
|
||||||
#### Step 2: Submit Test Tasks
|
#### Step 2: Submit Test Tasks
|
||||||
|
|
||||||
Use `submit_from_csv` to submit test tasks:
|
Use `submit_from_csv_export_ndax` to submit test tasks:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
result = device.submit_from_csv(
|
result = device.submit_from_csv_export_ndax(
|
||||||
csv_path="test_data.csv",
|
csv_path="test_data.csv",
|
||||||
output_dir="D:/neware_output"
|
output_dir="D:/neware_output"
|
||||||
)
|
)
|
||||||
@@ -500,7 +500,7 @@ A: Obtain a new API Key and update the `UNI_LAB_AUTH_TOKEN` environment variable
|
|||||||
**Q: Can I customize upload paths?**
|
**Q: Can I customize upload paths?**
|
||||||
A: Current version has paths automatically assigned by unified API. `oss_prefix` parameter is currently unused (retained for interface compatibility).
|
A: Current version has paths automatically assigned by unified API. `oss_prefix` parameter is currently unused (retained for interface compatibility).
|
||||||
|
|
||||||
**Q: Why not auto-upload in `submit_from_csv`?**
|
**Q: Why not auto-upload in `submit_from_csv_export_ndax`?**
|
||||||
A: Because backup files are generated progressively during testing, they may not be fully generated when the method returns. A separate upload method provides more flexibility.
|
A: Because backup files are generated progressively during testing, they may not be fully generated when the method returns. A separate upload method provides more flexibility.
|
||||||
|
|
||||||
**Q: How to access files after upload?**
|
**Q: How to access files after upload?**
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"功能说明": "新威电池测试系统,提供720通道监控和CSV批量提交功能",
|
"功能说明": "新威电池测试系统,提供720通道监控和CSV批量提交功能",
|
||||||
"监控功能": "支持720个通道的实时状态监控、2盘电池物料管理、状态导出等",
|
"监控功能": "支持720个通道的实时状态监控、2盘电池物料管理、状态导出等",
|
||||||
"提交功能": "通过submit_from_csv action从CSV文件批量提交测试任务。CSV必须包含: Battery_Code, Pole_Weight, 集流体质量, 活性物质含量, 克容量mah/g, 电池体系, 设备号, 排号, 通道号"
|
"提交功能": "通过submit_from_csv action从CSV文件批量提交测试任务(NDA备份),或通过submit_from_csv_export_excel action提交并备份为Excel格式。CSV必须包含: Battery_Code, Pole_Weight, 集流体质量, 活性物质含量, 克容量mah/g, 电池体系, 设备号, 排号, 通道号"
|
||||||
},
|
},
|
||||||
"children": []
|
"children": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1359,3 +1359,286 @@ def xml_ZQXNLRMO(act_mass, Cap_mAh):
|
|||||||
</root>
|
</root>
|
||||||
"""
|
"""
|
||||||
return xml_data
|
return xml_data
|
||||||
|
|
||||||
|
def xml_811_Li_JY(act_mass=None, Cap_mAh=None):
|
||||||
|
"""
|
||||||
|
生成XML内容
|
||||||
|
|
||||||
|
参数:
|
||||||
|
act_mass: 可选,未使用
|
||||||
|
Cap_mAh: 可选,未使用
|
||||||
|
"""
|
||||||
|
xml_data = f"""<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<config type="Step File" version="18" client_version="BTS Client 8.0.1.492(2025.01.23)(R3)" date="20251210133911" Guid="8a47521b-79f9-40e7-baaa-3e462f26979a">
|
||||||
|
<Head_Info>
|
||||||
|
<Operate Value="66" />
|
||||||
|
<Scale Value="1" />
|
||||||
|
<Start_Step Value="1" Hide_Ctrl_Step="0" />
|
||||||
|
<PN Value="2025-08-05 19-42-25" />
|
||||||
|
<RateType Value="103" />
|
||||||
|
</Head_Info>
|
||||||
|
<Whole_Prt>
|
||||||
|
<Record>
|
||||||
|
<Main>
|
||||||
|
<Time Value="30000" />
|
||||||
|
</Main>
|
||||||
|
</Record>
|
||||||
|
</Whole_Prt>
|
||||||
|
<Step_Info Num="13">
|
||||||
|
<Step1 Step_ID="1" Step_Type="4">
|
||||||
|
<Record>
|
||||||
|
<Main>
|
||||||
|
<Time Value="30000" />
|
||||||
|
</Main>
|
||||||
|
</Record>
|
||||||
|
<Limit>
|
||||||
|
<Main>
|
||||||
|
<Time Value="43200000" />
|
||||||
|
</Main>
|
||||||
|
</Limit>
|
||||||
|
<Protect>
|
||||||
|
<Main>
|
||||||
|
<Volt>
|
||||||
|
<Upper Value="50000" />
|
||||||
|
</Volt>
|
||||||
|
<EndVolt>
|
||||||
|
<Lower Value="-50000" />
|
||||||
|
</EndVolt>
|
||||||
|
</Main>
|
||||||
|
</Protect>
|
||||||
|
</Step1>
|
||||||
|
<Step2 Step_ID="2" Step_Type="1">
|
||||||
|
<Record>
|
||||||
|
<Main>
|
||||||
|
<Time Value="30000" />
|
||||||
|
</Main>
|
||||||
|
</Record>
|
||||||
|
<Limit>
|
||||||
|
<Main>
|
||||||
|
<Curr Value="0.206" />
|
||||||
|
<Stop_Volt Value="43000" />
|
||||||
|
</Main>
|
||||||
|
</Limit>
|
||||||
|
<Protect>
|
||||||
|
<Main>
|
||||||
|
<Volt>
|
||||||
|
<Upper Value="50000" />
|
||||||
|
</Volt>
|
||||||
|
<EndVolt>
|
||||||
|
<Lower Value="-50000" />
|
||||||
|
</EndVolt>
|
||||||
|
</Main>
|
||||||
|
</Protect>
|
||||||
|
</Step2>
|
||||||
|
<Step3 Step_ID="3" Step_Type="3">
|
||||||
|
<Record>
|
||||||
|
<Main>
|
||||||
|
<Time Value="30000" />
|
||||||
|
</Main>
|
||||||
|
</Record>
|
||||||
|
<Limit>
|
||||||
|
<Main>
|
||||||
|
<Curr Value="0.206" />
|
||||||
|
<Volt Value="43000" />
|
||||||
|
<Stop_Curr Value="0.05" />
|
||||||
|
</Main>
|
||||||
|
</Limit>
|
||||||
|
<Protect>
|
||||||
|
<Main>
|
||||||
|
<Volt>
|
||||||
|
<Upper Value="50000" />
|
||||||
|
</Volt>
|
||||||
|
<EndVolt>
|
||||||
|
<Lower Value="-50000" />
|
||||||
|
</EndVolt>
|
||||||
|
</Main>
|
||||||
|
</Protect>
|
||||||
|
</Step3>
|
||||||
|
<Step4 Step_ID="4" Step_Type="2">
|
||||||
|
<Record>
|
||||||
|
<Main>
|
||||||
|
<Time Value="30000" />
|
||||||
|
</Main>
|
||||||
|
</Record>
|
||||||
|
<Limit>
|
||||||
|
<Main>
|
||||||
|
<Curr Value="0.206" />
|
||||||
|
<Stop_Volt Value="27500" />
|
||||||
|
</Main>
|
||||||
|
</Limit>
|
||||||
|
<Protect>
|
||||||
|
<Main>
|
||||||
|
<Volt>
|
||||||
|
<Upper Value="50000" />
|
||||||
|
</Volt>
|
||||||
|
<EndVolt>
|
||||||
|
<Lower Value="-50000" />
|
||||||
|
</EndVolt>
|
||||||
|
</Main>
|
||||||
|
</Protect>
|
||||||
|
</Step4>
|
||||||
|
<Step5 Step_ID="5" Step_Type="1">
|
||||||
|
<Record>
|
||||||
|
<Main>
|
||||||
|
<Time Value="30000" />
|
||||||
|
</Main>
|
||||||
|
</Record>
|
||||||
|
<Limit>
|
||||||
|
<Main>
|
||||||
|
<Curr Value="0.206" />
|
||||||
|
<Stop_Volt Value="43000" />
|
||||||
|
</Main>
|
||||||
|
</Limit>
|
||||||
|
<Protect>
|
||||||
|
<Main>
|
||||||
|
<Volt>
|
||||||
|
<Upper Value="50000" />
|
||||||
|
</Volt>
|
||||||
|
<EndVolt>
|
||||||
|
<Lower Value="-50000" />
|
||||||
|
</EndVolt>
|
||||||
|
</Main>
|
||||||
|
</Protect>
|
||||||
|
</Step5>
|
||||||
|
<Step6 Step_ID="6" Step_Type="2">
|
||||||
|
<Record>
|
||||||
|
<Main>
|
||||||
|
<Time Value="30000" />
|
||||||
|
</Main>
|
||||||
|
</Record>
|
||||||
|
<Limit>
|
||||||
|
<Main>
|
||||||
|
<Curr Value="0.206" />
|
||||||
|
<Stop_Volt Value="27500" />
|
||||||
|
</Main>
|
||||||
|
</Limit>
|
||||||
|
<Protect>
|
||||||
|
<Main>
|
||||||
|
<Volt>
|
||||||
|
<Upper Value="50000" />
|
||||||
|
</Volt>
|
||||||
|
<EndVolt>
|
||||||
|
<Lower Value="-50000" />
|
||||||
|
</EndVolt>
|
||||||
|
</Main>
|
||||||
|
</Protect>
|
||||||
|
</Step6>
|
||||||
|
<Step7 Step_ID="7" Step_Type="1">
|
||||||
|
<Record>
|
||||||
|
<Main>
|
||||||
|
<Time Value="30000" />
|
||||||
|
</Main>
|
||||||
|
</Record>
|
||||||
|
<Limit>
|
||||||
|
<Main>
|
||||||
|
<Curr Value="1.03" />
|
||||||
|
<Stop_Volt Value="43000" />
|
||||||
|
</Main>
|
||||||
|
</Limit>
|
||||||
|
<Protect>
|
||||||
|
<Main>
|
||||||
|
<Volt>
|
||||||
|
<Upper Value="50000" />
|
||||||
|
</Volt>
|
||||||
|
<EndVolt>
|
||||||
|
<Lower Value="-50000" />
|
||||||
|
</EndVolt>
|
||||||
|
</Main>
|
||||||
|
</Protect>
|
||||||
|
</Step7>
|
||||||
|
<Step8 Step_ID="8" Step_Type="2">
|
||||||
|
<Record>
|
||||||
|
<Main>
|
||||||
|
<Time Value="30000" />
|
||||||
|
</Main>
|
||||||
|
</Record>
|
||||||
|
<Limit>
|
||||||
|
<Main>
|
||||||
|
<Curr Value="1.03" />
|
||||||
|
<Stop_Volt Value="27500" />
|
||||||
|
</Main>
|
||||||
|
</Limit>
|
||||||
|
<Protect>
|
||||||
|
<Main>
|
||||||
|
<Volt>
|
||||||
|
<Upper Value="50000" />
|
||||||
|
</Volt>
|
||||||
|
<EndVolt>
|
||||||
|
<Lower Value="-50000" />
|
||||||
|
</EndVolt>
|
||||||
|
</Main>
|
||||||
|
</Protect>
|
||||||
|
</Step8>
|
||||||
|
<Step9 Step_ID="9" Step_Type="5">
|
||||||
|
<Limit>
|
||||||
|
<Other>
|
||||||
|
<Start_Step Value="7" />
|
||||||
|
<Cycle_Count Value="5" />
|
||||||
|
</Other>
|
||||||
|
</Limit>
|
||||||
|
</Step9>
|
||||||
|
<Step10 Step_ID="10" Step_Type="1">
|
||||||
|
<Record>
|
||||||
|
<Main>
|
||||||
|
<Time Value="30000" />
|
||||||
|
</Main>
|
||||||
|
</Record>
|
||||||
|
<Limit>
|
||||||
|
<Main>
|
||||||
|
<Curr Value="2.06" />
|
||||||
|
<Stop_Volt Value="43000" />
|
||||||
|
</Main>
|
||||||
|
</Limit>
|
||||||
|
<Protect>
|
||||||
|
<Main>
|
||||||
|
<Volt>
|
||||||
|
<Upper Value="50000" />
|
||||||
|
</Volt>
|
||||||
|
<EndVolt>
|
||||||
|
<Lower Value="-50000" />
|
||||||
|
</EndVolt>
|
||||||
|
</Main>
|
||||||
|
</Protect>
|
||||||
|
</Step10>
|
||||||
|
<Step11 Step_ID="11" Step_Type="2">
|
||||||
|
<Record>
|
||||||
|
<Main>
|
||||||
|
<Time Value="30000" />
|
||||||
|
</Main>
|
||||||
|
</Record>
|
||||||
|
<Limit>
|
||||||
|
<Main>
|
||||||
|
<Curr Value="2.06" />
|
||||||
|
<Stop_Volt Value="27500" />
|
||||||
|
</Main>
|
||||||
|
</Limit>
|
||||||
|
<Protect>
|
||||||
|
<Main>
|
||||||
|
<Volt>
|
||||||
|
<Upper Value="50000" />
|
||||||
|
</Volt>
|
||||||
|
<EndVolt>
|
||||||
|
<Lower Value="-50000" />
|
||||||
|
</EndVolt>
|
||||||
|
</Main>
|
||||||
|
</Protect>
|
||||||
|
</Step11>
|
||||||
|
<Step12 Step_ID="12" Step_Type="5">
|
||||||
|
<Limit>
|
||||||
|
<Other>
|
||||||
|
<Start_Step Value="10" />
|
||||||
|
<Cycle_Count Value="500" />
|
||||||
|
</Other>
|
||||||
|
</Limit>
|
||||||
|
</Step12>
|
||||||
|
<Step13 Step_ID="13" Step_Type="6">
|
||||||
|
</Step13>
|
||||||
|
</Step_Info>
|
||||||
|
<SMBUS>
|
||||||
|
<SMBUS_Info Num="0" AdjacentInterval="0" />
|
||||||
|
</SMBUS>
|
||||||
|
</config>
|
||||||
|
</root>
|
||||||
|
"""
|
||||||
|
return xml_data
|
||||||
|
|||||||
@@ -19,10 +19,13 @@ import socket
|
|||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
|
import inspect
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, Dict, List, Optional, TypedDict
|
from typing import Any, Dict, List, Optional, TypedDict
|
||||||
|
|
||||||
from pylabrobot.resources import ResourceHolder, Coordinate, create_ordered_items_2d, Deck, Plate
|
from pylabrobot.resources import ResourceHolder, Coordinate, create_ordered_items_2d, Deck, Plate
|
||||||
|
from unilabos.registry.placeholder_type import ResourceSlot, DeviceSlot
|
||||||
|
from unilabos.resources.resource_tracker import ResourceTreeSet
|
||||||
from unilabos.ros.nodes.base_device_node import ROS2DeviceNode
|
from unilabos.ros.nodes.base_device_node import ROS2DeviceNode
|
||||||
from unilabos.ros.nodes.presets.workstation import ROS2WorkstationNode
|
from unilabos.ros.nodes.presets.workstation import ROS2WorkstationNode
|
||||||
|
|
||||||
@@ -256,12 +259,27 @@ class BatteryTestPosition(ResourceHolder):
|
|||||||
super().load_state(state)
|
super().load_state(state)
|
||||||
self._unilabos_state = state
|
self._unilabos_state = state
|
||||||
|
|
||||||
|
def serialize(self) -> dict:
|
||||||
|
d = super().serialize()
|
||||||
|
channel_name = self._unilabos_state.get("Channel_Name")
|
||||||
|
if channel_name:
|
||||||
|
d["name"] = channel_name
|
||||||
|
return d
|
||||||
|
|
||||||
def serialize_state(self) -> Dict[str, Dict[str, Any]]:
|
def serialize_state(self) -> Dict[str, Dict[str, Any]]:
|
||||||
"""格式不变"""
|
"""格式不变"""
|
||||||
data = super().serialize_state()
|
data = super().serialize_state()
|
||||||
data.update(self._unilabos_state)
|
data.update(self._unilabos_state)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def serialize_all_state(self) -> Dict[str, Dict[str, Any]]:
|
||||||
|
states = {}
|
||||||
|
channel_name = self._unilabos_state.get("Channel_Name", self.name)
|
||||||
|
states[channel_name] = self.serialize_state()
|
||||||
|
for child in self.children:
|
||||||
|
states.update(child.serialize_all_state())
|
||||||
|
return states
|
||||||
|
|
||||||
|
|
||||||
class NewareBatteryTestSystem:
|
class NewareBatteryTestSystem:
|
||||||
"""
|
"""
|
||||||
@@ -292,13 +310,13 @@ class NewareBatteryTestSystem:
|
|||||||
# ========================
|
# ========================
|
||||||
STATUS_SET = {"working", "stop", "finish", "protect", "pause", "false"}
|
STATUS_SET = {"working", "stop", "finish", "protect", "pause", "false"}
|
||||||
STATUS_COLOR = {
|
STATUS_COLOR = {
|
||||||
"working": "#22c55e", # 绿
|
"working": "#15803d", # 深绿
|
||||||
"stop": "#6b7280", # 灰
|
"stop": "#4b5563", # 深灰
|
||||||
"finish": "#3b82f6", # 蓝
|
"finish": "#1d4ed8", # 深蓝
|
||||||
"protect": "#ef4444", # 红
|
"protect": "#b91c1c", # 深红
|
||||||
"pause": "#f59e0b", # 橙
|
"pause": "#b45309", # 深橙
|
||||||
"false": "#9ca3af", # 不存在/无效
|
"false": "#6b7280", # 灰
|
||||||
"unknown": "#a855f7", # 未知
|
"unknown": "#7c3aed", # 深紫
|
||||||
}
|
}
|
||||||
|
|
||||||
# 字母常量
|
# 字母常量
|
||||||
@@ -409,10 +427,10 @@ class NewareBatteryTestSystem:
|
|||||||
"""设置物料管理系统"""
|
"""设置物料管理系统"""
|
||||||
deck_main = Deck(
|
deck_main = Deck(
|
||||||
name="ADeckName",
|
name="ADeckName",
|
||||||
size_x=2200,
|
size_x=1200,
|
||||||
size_y=2800,
|
size_y=2800,
|
||||||
size_z=100,
|
size_z=100,
|
||||||
origin=Coordinate(2000, 2000, 0)
|
origin=Coordinate(-5500, 0, 0)
|
||||||
)
|
)
|
||||||
self.station_resources = {}
|
self.station_resources = {}
|
||||||
self.station_resources_by_plate = {}
|
self.station_resources_by_plate = {}
|
||||||
@@ -432,19 +450,34 @@ class NewareBatteryTestSystem:
|
|||||||
plate_name = self._plate_name(devid, plate_num)
|
plate_name = self._plate_name(devid, plate_num)
|
||||||
plate = Plate(
|
plate = Plate(
|
||||||
name=plate_name,
|
name=plate_name,
|
||||||
size_x=400,
|
size_x=540,
|
||||||
size_y=300,
|
size_y=350,
|
||||||
size_z=50,
|
size_z=50,
|
||||||
ordered_items=plate_resources
|
ordered_items=plate_resources
|
||||||
)
|
)
|
||||||
location_x = 0 if plate_num == 1 else 450
|
location_x = 0 if plate_num == 1 else 590
|
||||||
location_y = row_idx * 350
|
location_y = row_idx * 400
|
||||||
deck_main.assign_child_resource(plate, location=Coordinate(location_x, location_y, 0))
|
deck_main.assign_child_resource(plate, location=Coordinate(location_x, location_y, 0))
|
||||||
|
|
||||||
plate_key = (devid, plate_num)
|
plate_key = (devid, plate_num)
|
||||||
|
subdev_start = 1 if plate_num == 1 else 6
|
||||||
self.station_resources_by_plate[plate_key] = {}
|
self.station_resources_by_plate[plate_key] = {}
|
||||||
for name, resource in plate_resources.items():
|
for name, resource in plate_resources.items():
|
||||||
new_name = f"{plate_name}_{name}"
|
new_name = f"{plate_name}_{name}"
|
||||||
|
# 从名称解析 col/row 索引,设置初始 Channel_Name
|
||||||
|
parts = name.rsplit("_", 2)
|
||||||
|
if len(parts) >= 3:
|
||||||
|
col_idx, row_idx = int(parts[-2]), int(parts[-1])
|
||||||
|
chl_id = col_idx + 1
|
||||||
|
subdev_id = subdev_start + row_idx
|
||||||
|
resource.load_state({
|
||||||
|
"status": "unknown",
|
||||||
|
"color": self.STATUS_COLOR["unknown"],
|
||||||
|
"voltage": 0.0,
|
||||||
|
"current": 0.0,
|
||||||
|
"time": 0.0,
|
||||||
|
"Channel_Name": f"{devid}-{subdev_id}-{chl_id}",
|
||||||
|
})
|
||||||
self.station_resources_by_plate[plate_key][new_name] = resource
|
self.station_resources_by_plate[plate_key][new_name] = resource
|
||||||
self.station_resources[new_name] = resource
|
self.station_resources[new_name] = resource
|
||||||
|
|
||||||
@@ -874,6 +907,28 @@ class NewareBatteryTestSystem:
|
|||||||
"""规范化电池体系名称"""
|
"""规范化电池体系名称"""
|
||||||
return str(bs).strip().replace('-', '_').upper()
|
return str(bs).strip().replace('-', '_').upper()
|
||||||
|
|
||||||
|
def _get_builder_required_positional_count(self, builder) -> int:
|
||||||
|
"""返回XML生成函数必填位置参数个数(仅统计无默认值的positional参数)"""
|
||||||
|
sig = inspect.signature(builder)
|
||||||
|
required = 0
|
||||||
|
for p in sig.parameters.values():
|
||||||
|
if p.kind in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD):
|
||||||
|
if p.default is inspect.Parameter.empty:
|
||||||
|
required += 1
|
||||||
|
return required
|
||||||
|
|
||||||
|
def _is_csv_value_empty(self, value) -> bool:
|
||||||
|
"""判断CSV单元格是否为空(兼容NaN/None/空串/null)"""
|
||||||
|
if value is None:
|
||||||
|
return True
|
||||||
|
if isinstance(value, str):
|
||||||
|
return value.strip().lower() in ("", "nan", "none", "null")
|
||||||
|
try:
|
||||||
|
# NaN 与自身不相等
|
||||||
|
return value != value
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
def _compute_values(self, row):
|
def _compute_values(self, row):
|
||||||
"""
|
"""
|
||||||
计算活性物质质量和容量
|
计算活性物质质量和容量
|
||||||
@@ -884,7 +939,7 @@ class NewareBatteryTestSystem:
|
|||||||
Returns:
|
Returns:
|
||||||
tuple: (活性物质质量mg, 容量mAh)
|
tuple: (活性物质质量mg, 容量mAh)
|
||||||
"""
|
"""
|
||||||
pw = float(row['Pole_Weight'])
|
pw = float(row['pole_weight'])
|
||||||
cm = float(row['集流体质量'])
|
cm = float(row['集流体质量'])
|
||||||
am = row['活性物质含量']
|
am = row['活性物质含量']
|
||||||
if isinstance(am, str) and am.endswith('%'):
|
if isinstance(am, str) and am.endswith('%'):
|
||||||
@@ -918,6 +973,7 @@ class NewareBatteryTestSystem:
|
|||||||
'SIGR_LI': gen_mod.xml_SiGr_Li_Step,
|
'SIGR_LI': gen_mod.xml_SiGr_Li_Step,
|
||||||
'811_SIGR': gen_mod.xml_811_SiGr,
|
'811_SIGR': gen_mod.xml_811_SiGr,
|
||||||
'811_CU_AGING': gen_mod.xml_811_Cu_aging,
|
'811_CU_AGING': gen_mod.xml_811_Cu_aging,
|
||||||
|
'811_LI_JY': gen_mod.xml_811_Li_JY,
|
||||||
'ZQXNLRMO':gen_mod.xml_ZQXNLRMO,
|
'ZQXNLRMO':gen_mod.xml_ZQXNLRMO,
|
||||||
}
|
}
|
||||||
if key not in fmap:
|
if key not in fmap:
|
||||||
@@ -935,7 +991,7 @@ class NewareBatteryTestSystem:
|
|||||||
with open(path, 'w', encoding='utf-8') as f:
|
with open(path, 'w', encoding='utf-8') as f:
|
||||||
f.write(xml)
|
f.write(xml)
|
||||||
|
|
||||||
def submit_from_csv(self, csv_path: str, output_dir: str = ".") -> dict:
|
def submit_from_csv_export_ndax(self, csv_path: str, output_dir: str = ".") -> dict:
|
||||||
"""
|
"""
|
||||||
从CSV文件批量提交Neware测试任务(设备动作)
|
从CSV文件批量提交Neware测试任务(设备动作)
|
||||||
|
|
||||||
@@ -967,8 +1023,7 @@ class NewareBatteryTestSystem:
|
|||||||
|
|
||||||
# 验证必需列
|
# 验证必需列
|
||||||
required = [
|
required = [
|
||||||
'Battery_Code', 'Electrolyte_Code', 'Pole_Weight', '集流体质量', '活性物质含量',
|
'coin_cell_code', 'electrolyte_code', '电池体系', '设备号', '排号', '通道号'
|
||||||
'克容量mah/g', '电池体系', '设备号', '排号', '通道号'
|
|
||||||
]
|
]
|
||||||
missing = [c for c in required if c not in df.columns]
|
missing = [c for c in required if c not in df.columns]
|
||||||
if missing:
|
if missing:
|
||||||
@@ -997,27 +1052,47 @@ class NewareBatteryTestSystem:
|
|||||||
|
|
||||||
for idx, row in df.iterrows():
|
for idx, row in df.iterrows():
|
||||||
try:
|
try:
|
||||||
coin_id = f"{row['Battery_Code']}-{row['Electrolyte_Code']}"
|
coin_id = f"{row['coin_cell_code']}-{row['electrolyte_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生成函数
|
# 获取电池体系对应的XML生成函数
|
||||||
key = self._canon(row['电池体系'])
|
key = self._canon(row['电池体系'])
|
||||||
builder = self._get_xml_builder(gen_mod, key)
|
builder = self._get_xml_builder(gen_mod, key)
|
||||||
|
builder_required_args = self._get_builder_required_positional_count(builder)
|
||||||
|
|
||||||
# 生成XML内容
|
# 生成XML内容:仅当工步模板需要时才校验并计算 act_mass/cap_mAh
|
||||||
xml_content = builder(act_mass, cap_mAh)
|
if builder_required_args == 0:
|
||||||
|
xml_content = builder()
|
||||||
|
elif builder_required_args == 2:
|
||||||
|
calc_cols = ['pole_weight', '集流体质量', '活性物质含量', '克容量mah/g']
|
||||||
|
missing_calc = [
|
||||||
|
c for c in calc_cols
|
||||||
|
if c not in df.columns or self._is_csv_value_empty(row[c])
|
||||||
|
]
|
||||||
|
if missing_calc:
|
||||||
|
error_msg = (
|
||||||
|
f"电池体系 {key} 需要 act_mass/Cap_mAh,以下列缺失或为空: {missing_calc}, "
|
||||||
|
f"CoinID={coin_id}"
|
||||||
|
)
|
||||||
|
if self._ros_node:
|
||||||
|
self._ros_node.lab_logger().warning(error_msg)
|
||||||
|
results.append(f"行{idx+1} 失败: {error_msg}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
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_content = builder(act_mass, cap_mAh)
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"XML生成函数参数不支持: {builder.__name__} 需要 {builder_required_args} 个必填位置参数"
|
||||||
|
)
|
||||||
|
|
||||||
# 获取设备信息
|
# 获取设备信息
|
||||||
devid = int(row['设备号'])
|
devid = int(row['设备号'])
|
||||||
@@ -1040,7 +1115,8 @@ class NewareBatteryTestSystem:
|
|||||||
chlid=chlid,
|
chlid=chlid,
|
||||||
CoinID=coin_id,
|
CoinID=coin_id,
|
||||||
recipe_path=recipe_path,
|
recipe_path=recipe_path,
|
||||||
backup_dir=backup_dir
|
backup_dir=backup_dir,
|
||||||
|
filetype=0
|
||||||
)
|
)
|
||||||
|
|
||||||
submitted_count += 1
|
submitted_count += 1
|
||||||
@@ -1048,7 +1124,7 @@ class NewareBatteryTestSystem:
|
|||||||
|
|
||||||
if self._ros_node:
|
if self._ros_node:
|
||||||
self._ros_node.lab_logger().info(
|
self._ros_node.lab_logger().info(
|
||||||
f"已提交 {coin_id} (设备{devid}-{subdevid}-{chlid}): {resp}"
|
f"已提交 {coin_id} (设备{devid}-{subdevid}-{chlid}, NDAX备份): {resp}"
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -1088,6 +1164,168 @@ class NewareBatteryTestSystem:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def submit_from_csv_export_excel(self, csv_path: str, output_dir: str = ".") -> dict:
|
||||||
|
"""
|
||||||
|
从CSV文件批量提交Neware测试任务,备份格式为Excel(设备动作)
|
||||||
|
|
||||||
|
与 submit_from_csv_export_ndax 逻辑一致,唯一区别是 BTS 备份文件格式为 Excel 而非 NDA。
|
||||||
|
|
||||||
|
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文件提交任务(Excel备份): {csv_path}")
|
||||||
|
|
||||||
|
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, "total_count": 0}
|
||||||
|
|
||||||
|
df = pd.read_csv(csv_path, encoding='gbk')
|
||||||
|
|
||||||
|
required = [
|
||||||
|
'coin_cell_code', 'electrolyte_code', '电池体系', '设备号', '排号', '通道号'
|
||||||
|
]
|
||||||
|
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, "total_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)
|
||||||
|
|
||||||
|
self._last_backup_dir = backup_dir
|
||||||
|
|
||||||
|
if self._ros_node:
|
||||||
|
self._ros_node.lab_logger().info(
|
||||||
|
f"输出目录: XML={xml_dir}, 备份(Excel)={backup_dir}"
|
||||||
|
)
|
||||||
|
|
||||||
|
submitted_count = 0
|
||||||
|
results = []
|
||||||
|
|
||||||
|
for idx, row in df.iterrows():
|
||||||
|
try:
|
||||||
|
coin_id = f"{row['coin_cell_code']}-{row['electrolyte_code']}"
|
||||||
|
|
||||||
|
key = self._canon(row['电池体系'])
|
||||||
|
builder = self._get_xml_builder(gen_mod, key)
|
||||||
|
builder_required_args = self._get_builder_required_positional_count(builder)
|
||||||
|
|
||||||
|
if builder_required_args == 0:
|
||||||
|
xml_content = builder()
|
||||||
|
elif builder_required_args == 2:
|
||||||
|
calc_cols = ['pole_weight', '集流体质量', '活性物质含量', '克容量mah/g']
|
||||||
|
missing_calc = [
|
||||||
|
c for c in calc_cols
|
||||||
|
if c not in df.columns or self._is_csv_value_empty(row[c])
|
||||||
|
]
|
||||||
|
if missing_calc:
|
||||||
|
error_msg = (
|
||||||
|
f"电池体系 {key} 需要 act_mass/Cap_mAh,以下列缺失或为空: {missing_calc}, "
|
||||||
|
f"CoinID={coin_id}"
|
||||||
|
)
|
||||||
|
if self._ros_node:
|
||||||
|
self._ros_node.lab_logger().warning(error_msg)
|
||||||
|
results.append(f"行{idx+1} 失败: {error_msg}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
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_content = builder(act_mass, cap_mAh)
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"XML生成函数参数不支持: {builder.__name__} 需要 {builder_required_args} 个必填位置参数"
|
||||||
|
)
|
||||||
|
|
||||||
|
devid = int(row['设备号'])
|
||||||
|
subdevid = int(row['排号'])
|
||||||
|
chlid = int(row['通道号'])
|
||||||
|
|
||||||
|
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,
|
||||||
|
filetype=1
|
||||||
|
)
|
||||||
|
|
||||||
|
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}, Excel备份): {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"批量提交完成(Excel备份): 成功{submitted_count}个,共{len(df)}行。"
|
||||||
|
f"\n详细结果:\n" + "\n".join(results)
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._ros_node:
|
||||||
|
self._ros_node.lab_logger().info(
|
||||||
|
f"批量提交完成(Excel备份): 成功{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"批量提交失败(Excel备份): {str(e)}"
|
||||||
|
if self._ros_node:
|
||||||
|
self._ros_node.lab_logger().error(error_msg)
|
||||||
|
return {
|
||||||
|
"return_info": error_msg,
|
||||||
|
"success": False,
|
||||||
|
"submitted_count": 0,
|
||||||
|
"total_count": 0
|
||||||
|
}
|
||||||
|
|
||||||
def get_device_summary(self) -> dict:
|
def get_device_summary(self) -> dict:
|
||||||
"""
|
"""
|
||||||
获取设备级别的摘要统计(设备动作)
|
获取设备级别的摘要统计(设备动作)
|
||||||
@@ -1164,7 +1402,7 @@ class NewareBatteryTestSystem:
|
|||||||
上传备份目录中的文件到 OSS(ROS2 动作)
|
上传备份目录中的文件到 OSS(ROS2 动作)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
backup_dir: 备份目录路径,默认使用最近一次 submit_from_csv 的 backup_dir
|
backup_dir: 备份目录路径,默认使用最近一次提交任务的 backup_dir
|
||||||
file_pattern: 文件通配符模式,默认 "*" 上传所有文件(例如 "*.csv" 仅上传 CSV 文件)
|
file_pattern: 文件通配符模式,默认 "*" 上传所有文件(例如 "*.csv" 仅上传 CSV 文件)
|
||||||
oss_prefix: OSS 对象前缀,默认使用类初始化时的配置
|
oss_prefix: OSS 对象前缀,默认使用类初始化时的配置
|
||||||
|
|
||||||
@@ -1694,6 +1932,235 @@ class NewareBatteryTestSystem:
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def manual_confirm(
|
||||||
|
self,
|
||||||
|
resource: List[ResourceSlot],
|
||||||
|
target_device: DeviceSlot,
|
||||||
|
mount_resource: List[ResourceSlot],
|
||||||
|
collector_mass: List[float],
|
||||||
|
active_material: List[float],
|
||||||
|
capacity: List[float],
|
||||||
|
battery_system: List[str],
|
||||||
|
timeout_seconds: int,
|
||||||
|
assignee_user_ids: list[str],
|
||||||
|
**kwargs
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
timeout_seconds: 超时时间(秒),默认3600秒
|
||||||
|
collector_mass: 极流体质量
|
||||||
|
active_material: 活性物质含量
|
||||||
|
capacity: 克容量(mAh/g)
|
||||||
|
battery_system: 电池体系
|
||||||
|
修改的结果无效,是只读的
|
||||||
|
"""
|
||||||
|
resource = ResourceTreeSet.from_plr_resources(resource).dump()
|
||||||
|
mount_resource = ResourceTreeSet.from_plr_resources(mount_resource).dump()
|
||||||
|
kwargs.update(locals())
|
||||||
|
kwargs.pop("kwargs")
|
||||||
|
kwargs.pop("self")
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
|
async def transfer(self, resource: List[ResourceSlot], target_device: DeviceSlot, mount_resource: List[ResourceSlot]):
|
||||||
|
future = ROS2DeviceNode.run_async_func(self._ros_node.transfer_resource_to_another, True,
|
||||||
|
**{
|
||||||
|
"plr_resources": resource,
|
||||||
|
"target_device_id": target_device,
|
||||||
|
"target_resources": mount_resource,
|
||||||
|
"sites": [None] * len(mount_resource),
|
||||||
|
})
|
||||||
|
result = await future
|
||||||
|
return result
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# test() 辅助方法
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_channel_name(res) -> Optional[str]:
|
||||||
|
"""从 BatteryTestPosition 或通用 Resource 中提取 Channel_Name (devid-subdevid-chlid)"""
|
||||||
|
# 情况1: ResourceSlot 对象 —— 直接读 _unilabos_state
|
||||||
|
state = getattr(res, "_unilabos_state", None)
|
||||||
|
if isinstance(state, dict):
|
||||||
|
ch = state.get("Channel_Name")
|
||||||
|
if ch:
|
||||||
|
return str(ch)
|
||||||
|
# 情况2: serialize_state()
|
||||||
|
if hasattr(res, "serialize_state"):
|
||||||
|
try:
|
||||||
|
ss = res.serialize_state()
|
||||||
|
if isinstance(ss, dict):
|
||||||
|
ch = ss.get("Channel_Name")
|
||||||
|
if ch:
|
||||||
|
return str(ch)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
# 情况3: 来自 ResourceTreeSet.dump() 的 dict
|
||||||
|
if isinstance(res, dict):
|
||||||
|
data = res.get("data", {})
|
||||||
|
if isinstance(data, dict):
|
||||||
|
ch = data.get("Channel_Name")
|
||||||
|
if ch:
|
||||||
|
return str(ch)
|
||||||
|
ch = res.get("name") or res.get("id")
|
||||||
|
if ch and len(str(ch).split("-")) == 3:
|
||||||
|
return str(ch)
|
||||||
|
# 情况4: name 本身就是 "devid-subdevid-chlid"
|
||||||
|
name = getattr(res, "name", "")
|
||||||
|
if name and len(name.split("-")) == 3:
|
||||||
|
return name
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_pole_weight(res) -> float:
|
||||||
|
"""从电池资源 state 中提取极片称重 (mg)"""
|
||||||
|
state = getattr(res, "_unilabos_state", None)
|
||||||
|
if isinstance(state, dict) and "pole_weight" in state:
|
||||||
|
return float(state["pole_weight"])
|
||||||
|
if hasattr(res, "serialize_state"):
|
||||||
|
try:
|
||||||
|
ss = res.serialize_state()
|
||||||
|
if isinstance(ss, dict) and "pole_weight" in ss:
|
||||||
|
return float(ss["pole_weight"])
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if isinstance(res, dict):
|
||||||
|
data = res.get("data", {})
|
||||||
|
if isinstance(data, dict) and "pole_weight" in data:
|
||||||
|
return float(data["pole_weight"])
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _parse_active_material(val) -> float:
|
||||||
|
"""解析活性物质含量,支持 0.97 或 '97%' 两种格式"""
|
||||||
|
if isinstance(val, str):
|
||||||
|
val = val.strip()
|
||||||
|
if val.endswith("%"):
|
||||||
|
return float(val[:-1]) / 100.0
|
||||||
|
return float(val)
|
||||||
|
return float(val)
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# test 动作:下发测试
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
async def test(
|
||||||
|
self,
|
||||||
|
resource: List[ResourceSlot],
|
||||||
|
mount_resource: List[ResourceSlot],
|
||||||
|
collector_mass: List[float],
|
||||||
|
active_material: List[float],
|
||||||
|
capacity: List[float],
|
||||||
|
battery_system: List[str],
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
对每颗电池计算测试参数、生成 XML 工步文件并通过 TCP 下发给新威测试仪。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
resource: 成品电池资源列表(含 pole_weight 状态)
|
||||||
|
mount_resource: 目标通道资源列表(含 Channel_Name = devid-subdevid-chlid)
|
||||||
|
collector_mass: 各电池集流体质量 (mg)
|
||||||
|
active_material: 各电池活性物质比例(0.97 或 "97%")
|
||||||
|
capacity: 各电池克容量 (mAh/g)
|
||||||
|
battery_system: 各电池体系名称(如 "811_LI_002")
|
||||||
|
"""
|
||||||
|
import importlib
|
||||||
|
gen_mod = importlib.import_module(
|
||||||
|
"unilabos.devices.neware_battery_test_system.generate_xml_content"
|
||||||
|
)
|
||||||
|
from .neware_driver import start_test as _start_test
|
||||||
|
|
||||||
|
n = len(resource)
|
||||||
|
results = []
|
||||||
|
submitted = 0
|
||||||
|
|
||||||
|
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")
|
||||||
|
os.makedirs(backup_dir, exist_ok=True)
|
||||||
|
|
||||||
|
for i in range(n):
|
||||||
|
try:
|
||||||
|
# 1. 解析通道地址
|
||||||
|
ch_name = self._extract_channel_name(mount_resource[i])
|
||||||
|
if not ch_name:
|
||||||
|
raise ValueError(f"无法从 mount_resource[{i}] 提取 Channel_Name")
|
||||||
|
parts = ch_name.split("-")
|
||||||
|
if len(parts) != 3:
|
||||||
|
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]
|
||||||
|
coin_id = (
|
||||||
|
getattr(res, "name", None)
|
||||||
|
or (res.get("name") if isinstance(res, dict) else None)
|
||||||
|
or f"battery_{i}"
|
||||||
|
)
|
||||||
|
pw = self._extract_pole_weight(res)
|
||||||
|
|
||||||
|
# 3. 计算活性物质质量与容量
|
||||||
|
cm = float(collector_mass[i])
|
||||||
|
amv = self._parse_active_material(active_material[i])
|
||||||
|
sc = float(capacity[i])
|
||||||
|
act_mass = round((pw - cm) * amv, 4)
|
||||||
|
if act_mass <= 0:
|
||||||
|
raise ValueError(
|
||||||
|
f"活性物质质量异常: pole_weight={pw}mg, collector_mass={cm}mg, "
|
||||||
|
f"active_material={amv}, act_mass={act_mass}"
|
||||||
|
)
|
||||||
|
cap_mAh = round(act_mass * sc / 1000.0, 4)
|
||||||
|
if cap_mAh <= 0:
|
||||||
|
raise ValueError(f"容量计算异常: act_mass={act_mass}mg, capacity={sc}mAh/g, cap_mAh={cap_mAh}")
|
||||||
|
|
||||||
|
# 4. 生成 XML 工步文件
|
||||||
|
key = self._canon(battery_system[i])
|
||||||
|
builder = self._get_xml_builder(gen_mod, key)
|
||||||
|
req_args = self._get_builder_required_positional_count(builder)
|
||||||
|
xml_content = builder(act_mass, cap_mAh) if req_args >= 2 else builder()
|
||||||
|
recipe_path = os.path.join(xml_dir, f"{coin_id}_{devid}_{subdevid}_{chlid}.xml")
|
||||||
|
self._save_xml(xml_content, recipe_path)
|
||||||
|
|
||||||
|
# 5. TCP 下发测试
|
||||||
|
resp = _start_test(
|
||||||
|
ip=self.ip,
|
||||||
|
port=int(self.port),
|
||||||
|
devid=devid,
|
||||||
|
subdevid=subdevid,
|
||||||
|
chlid=chlid,
|
||||||
|
CoinID=coin_id,
|
||||||
|
recipe_path=recipe_path,
|
||||||
|
backup_dir=backup_dir,
|
||||||
|
filetype=0,
|
||||||
|
)
|
||||||
|
submitted += 1
|
||||||
|
results.append({
|
||||||
|
"index": i,
|
||||||
|
"coin_id": coin_id,
|
||||||
|
"channel": ch_name,
|
||||||
|
"act_mass_mg": act_mass,
|
||||||
|
"cap_mAh": cap_mAh,
|
||||||
|
"success": True,
|
||||||
|
"response": str(resp)[:300],
|
||||||
|
})
|
||||||
|
if self._ros_node:
|
||||||
|
self._ros_node.lab_logger().info(
|
||||||
|
f"[test] 已下发 {coin_id} → {ch_name} "
|
||||||
|
f"act_mass={act_mass}mg cap={cap_mAh}mAh"
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
if self._ros_node:
|
||||||
|
self._ros_node.lab_logger().error(f"[test] 电池[{i}] 下发失败: {e}")
|
||||||
|
results.append({"index": i, "success": False, "error": str(e)})
|
||||||
|
|
||||||
|
summary = f"共 {n} 颗电池,成功下发 {submitted} 颗"
|
||||||
|
return {
|
||||||
|
"return_info": summary,
|
||||||
|
"success": submitted > 0,
|
||||||
|
"submitted_count": submitted,
|
||||||
|
"total_count": n,
|
||||||
|
"results": results,
|
||||||
|
}
|
||||||
|
|
||||||
# ========================
|
# ========================
|
||||||
# 示例和测试代码
|
# 示例和测试代码
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ neware_battery_test_system:
|
|||||||
title: StrSingleInput
|
title: StrSingleInput
|
||||||
type: object
|
type: object
|
||||||
type: StrSingleInput
|
type: StrSingleInput
|
||||||
submit_from_csv:
|
submit_from_csv_export_ndax:
|
||||||
feedback: {}
|
feedback: {}
|
||||||
goal:
|
goal:
|
||||||
csv_path: string
|
csv_path: string
|
||||||
@@ -231,7 +231,7 @@ neware_battery_test_system:
|
|||||||
placeholder_keys: {}
|
placeholder_keys: {}
|
||||||
result: {}
|
result: {}
|
||||||
schema:
|
schema:
|
||||||
description: 从CSV文件批量提交Neware测试任务
|
description: 从CSV文件批量提交Neware测试任务(备份格式为NDA)
|
||||||
properties:
|
properties:
|
||||||
feedback: {}
|
feedback: {}
|
||||||
goal:
|
goal:
|
||||||
@@ -250,7 +250,41 @@ neware_battery_test_system:
|
|||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- goal
|
- goal
|
||||||
title: submit_from_csv参数
|
title: submit_from_csv_export_ndax参数
|
||||||
|
type: object
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
submit_from_csv_export_excel:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
csv_path: string
|
||||||
|
output_dir: string
|
||||||
|
goal_default:
|
||||||
|
csv_path: null
|
||||||
|
output_dir: .
|
||||||
|
handles: {}
|
||||||
|
placeholder_keys: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
description: 从CSV文件批量提交Neware测试任务(备份格式为Excel)
|
||||||
|
properties:
|
||||||
|
feedback: {}
|
||||||
|
goal:
|
||||||
|
properties:
|
||||||
|
csv_path:
|
||||||
|
description: 输入CSV文件的绝对路径
|
||||||
|
type: string
|
||||||
|
output_dir:
|
||||||
|
default: .
|
||||||
|
description: 输出目录(用于存储XML和备份文件),默认当前目录
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- csv_path
|
||||||
|
type: object
|
||||||
|
result:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
title: submit_from_csv_export_excel参数
|
||||||
type: object
|
type: object
|
||||||
type: UniLabJsonCommand
|
type: UniLabJsonCommand
|
||||||
test_connection_action:
|
test_connection_action:
|
||||||
@@ -302,7 +336,7 @@ neware_battery_test_system:
|
|||||||
goal:
|
goal:
|
||||||
properties:
|
properties:
|
||||||
backup_dir:
|
backup_dir:
|
||||||
description: 备份目录路径(默认使用最近一次submit_from_csv的backup_dir)
|
description: 备份目录路径(默认使用最近一次提交任务的backup_dir)
|
||||||
type: string
|
type: string
|
||||||
file_pattern:
|
file_pattern:
|
||||||
default: '*'
|
default: '*'
|
||||||
@@ -320,6 +354,847 @@ neware_battery_test_system:
|
|||||||
title: upload_backup_to_oss参数
|
title: upload_backup_to_oss参数
|
||||||
type: object
|
type: object
|
||||||
type: UniLabJsonCommand
|
type: UniLabJsonCommand
|
||||||
|
manual_confirm:
|
||||||
|
type: UniLabJsonCommand
|
||||||
|
goal:
|
||||||
|
resource: resource
|
||||||
|
target_device: target_device
|
||||||
|
mount_resource: mount_resource
|
||||||
|
collector_mass: collector_mass
|
||||||
|
active_material: active_material
|
||||||
|
capacity: capacity
|
||||||
|
battery_system: battery_system
|
||||||
|
timeout_seconds: timeout_seconds
|
||||||
|
assignee_user_ids: assignee_user_ids
|
||||||
|
feedback: {}
|
||||||
|
result:
|
||||||
|
resource: resource
|
||||||
|
target_device: target_device
|
||||||
|
mount_resource: mount_resource
|
||||||
|
collector_mass: collector_mass
|
||||||
|
active_material: active_material
|
||||||
|
capacity: capacity
|
||||||
|
battery_system: battery_system
|
||||||
|
schema:
|
||||||
|
title: manual_confirm参数
|
||||||
|
description: manual_confirm的参数schema
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
goal:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
unilabos_device_id:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: UniLabOS设备ID,用于指定执行动作的具体设备实例
|
||||||
|
resource:
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
sample_id:
|
||||||
|
type: string
|
||||||
|
children:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
parent:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
category:
|
||||||
|
type: string
|
||||||
|
pose:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
position:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
x:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
y:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
z:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
required:
|
||||||
|
- x
|
||||||
|
- y
|
||||||
|
- z
|
||||||
|
title: position
|
||||||
|
additionalProperties: false
|
||||||
|
orientation:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
x:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
y:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
z:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
w:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
required:
|
||||||
|
- x
|
||||||
|
- y
|
||||||
|
- z
|
||||||
|
- w
|
||||||
|
title: orientation
|
||||||
|
additionalProperties: false
|
||||||
|
required:
|
||||||
|
- position
|
||||||
|
- orientation
|
||||||
|
title: pose
|
||||||
|
additionalProperties: false
|
||||||
|
config:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
type: string
|
||||||
|
title: resource
|
||||||
|
type: array
|
||||||
|
target_device:
|
||||||
|
type: string
|
||||||
|
description: device reference
|
||||||
|
mount_resource:
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
sample_id:
|
||||||
|
type: string
|
||||||
|
children:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
parent:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
category:
|
||||||
|
type: string
|
||||||
|
pose:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
position:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
x:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
y:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
z:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
required:
|
||||||
|
- x
|
||||||
|
- y
|
||||||
|
- z
|
||||||
|
title: position
|
||||||
|
additionalProperties: false
|
||||||
|
orientation:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
x:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
y:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
z:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
w:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
required:
|
||||||
|
- x
|
||||||
|
- y
|
||||||
|
- z
|
||||||
|
- w
|
||||||
|
title: orientation
|
||||||
|
additionalProperties: false
|
||||||
|
required:
|
||||||
|
- position
|
||||||
|
- orientation
|
||||||
|
title: pose
|
||||||
|
additionalProperties: false
|
||||||
|
config:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
type: string
|
||||||
|
title: mount_resource
|
||||||
|
type: array
|
||||||
|
collector_mass:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: number
|
||||||
|
active_material:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: number
|
||||||
|
capacity:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: number
|
||||||
|
battery_system:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
timeout_seconds:
|
||||||
|
type: integer
|
||||||
|
assignee_user_ids:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- resource
|
||||||
|
- target_device
|
||||||
|
- mount_resource
|
||||||
|
- collector_mass
|
||||||
|
- active_material
|
||||||
|
- capacity
|
||||||
|
- battery_system
|
||||||
|
- timeout_seconds
|
||||||
|
- assignee_user_ids
|
||||||
|
_unilabos_placeholder_info:
|
||||||
|
resource: unilabos_resources
|
||||||
|
target_device: unilabos_devices
|
||||||
|
mount_resource: unilabos_resources
|
||||||
|
assignee_user_ids: unilabos_manual_confirm
|
||||||
|
feedback: {}
|
||||||
|
result:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
goal_default:
|
||||||
|
resource: []
|
||||||
|
target_device: ''
|
||||||
|
mount_resource: []
|
||||||
|
collector_mass: []
|
||||||
|
active_material: []
|
||||||
|
capacity: []
|
||||||
|
battery_system: []
|
||||||
|
timeout_seconds: 3600
|
||||||
|
assignee_user_ids: []
|
||||||
|
handles:
|
||||||
|
input:
|
||||||
|
- handler_key: target_device
|
||||||
|
data_type: device_id
|
||||||
|
label: 目标设备
|
||||||
|
data_key: target_device
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
- handler_key: resource
|
||||||
|
data_type: resource
|
||||||
|
label: 待转移资源
|
||||||
|
data_key: resource
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
- handler_key: mount_resource
|
||||||
|
data_type: resource
|
||||||
|
label: 目标孔位
|
||||||
|
data_key: mount_resource
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
- handler_key: collector_mass
|
||||||
|
data_type: collector_mass
|
||||||
|
label: 极流体质量
|
||||||
|
data_key: collector_mass
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
- handler_key: active_material
|
||||||
|
data_type: active_material
|
||||||
|
label: 活性物质含量
|
||||||
|
data_key: active_material
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
- handler_key: capacity
|
||||||
|
data_type: capacity
|
||||||
|
label: 克容量
|
||||||
|
data_key: capacity
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
- handler_key: battery_system
|
||||||
|
data_type: battery_system
|
||||||
|
label: 电池体系
|
||||||
|
data_key: battery_system
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
output:
|
||||||
|
- handler_key: target_device
|
||||||
|
data_type: device_id
|
||||||
|
label: 目标设备
|
||||||
|
data_key: target_device
|
||||||
|
data_source: executor
|
||||||
|
- handler_key: resource
|
||||||
|
data_type: resource
|
||||||
|
label: 待转移资源
|
||||||
|
data_key: resource.@flatten
|
||||||
|
data_source: executor
|
||||||
|
- handler_key: mount_resource
|
||||||
|
data_type: resource
|
||||||
|
label: 目标孔位
|
||||||
|
data_key: mount_resource.@flatten
|
||||||
|
data_source: executor
|
||||||
|
- handler_key: collector_mass
|
||||||
|
data_type: collector_mass
|
||||||
|
label: 极流体质量
|
||||||
|
data_key: collector_mass
|
||||||
|
data_source: executor
|
||||||
|
- handler_key: active_material
|
||||||
|
data_type: active_material
|
||||||
|
label: 活性物质含量
|
||||||
|
data_key: active_material
|
||||||
|
data_source: executor
|
||||||
|
- handler_key: capacity
|
||||||
|
data_type: capacity
|
||||||
|
label: 克容量
|
||||||
|
data_key: capacity
|
||||||
|
data_source: executor
|
||||||
|
- handler_key: battery_system
|
||||||
|
data_type: battery_system
|
||||||
|
label: 电池体系
|
||||||
|
data_key: battery_system
|
||||||
|
data_source: executor
|
||||||
|
placeholder_keys:
|
||||||
|
resource: unilabos_resources
|
||||||
|
target_device: unilabos_devices
|
||||||
|
mount_resource: unilabos_resources
|
||||||
|
assignee_user_ids: unilabos_manual_confirm
|
||||||
|
always_free: true
|
||||||
|
feedback_interval: 300
|
||||||
|
node_type: manual_confirm
|
||||||
|
test:
|
||||||
|
type: UniLabJsonCommandAsync
|
||||||
|
goal:
|
||||||
|
resource: resource
|
||||||
|
mount_resource: mount_resource
|
||||||
|
collector_mass: collector_mass
|
||||||
|
active_material: active_material
|
||||||
|
capacity: capacity
|
||||||
|
battery_system: battery_system
|
||||||
|
feedback: {}
|
||||||
|
result:
|
||||||
|
return_info: return_info
|
||||||
|
success: success
|
||||||
|
submitted_count: submitted_count
|
||||||
|
total_count: total_count
|
||||||
|
results: results
|
||||||
|
schema:
|
||||||
|
title: test参数
|
||||||
|
description: test的参数schema
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
goal:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
unilabos_device_id:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: UniLabOS设备ID,用于指定执行动作的具体设备实例
|
||||||
|
resource:
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
sample_id:
|
||||||
|
type: string
|
||||||
|
children:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
parent:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
category:
|
||||||
|
type: string
|
||||||
|
pose:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
position:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
x:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
y:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
z:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
required:
|
||||||
|
- x
|
||||||
|
- y
|
||||||
|
- z
|
||||||
|
title: position
|
||||||
|
additionalProperties: false
|
||||||
|
orientation:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
x:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
y:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
z:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
w:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
required:
|
||||||
|
- x
|
||||||
|
- y
|
||||||
|
- z
|
||||||
|
- w
|
||||||
|
title: orientation
|
||||||
|
additionalProperties: false
|
||||||
|
required:
|
||||||
|
- position
|
||||||
|
- orientation
|
||||||
|
title: pose
|
||||||
|
additionalProperties: false
|
||||||
|
config:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
type: string
|
||||||
|
title: resource
|
||||||
|
type: array
|
||||||
|
mount_resource:
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
sample_id:
|
||||||
|
type: string
|
||||||
|
children:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
parent:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
category:
|
||||||
|
type: string
|
||||||
|
pose:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
position:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
x:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
y:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
z:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
required:
|
||||||
|
- x
|
||||||
|
- y
|
||||||
|
- z
|
||||||
|
title: position
|
||||||
|
additionalProperties: false
|
||||||
|
orientation:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
x:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
y:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
z:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
w:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
required:
|
||||||
|
- x
|
||||||
|
- y
|
||||||
|
- z
|
||||||
|
- w
|
||||||
|
title: orientation
|
||||||
|
additionalProperties: false
|
||||||
|
required:
|
||||||
|
- position
|
||||||
|
- orientation
|
||||||
|
title: pose
|
||||||
|
additionalProperties: false
|
||||||
|
config:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
type: string
|
||||||
|
title: mount_resource
|
||||||
|
type: array
|
||||||
|
collector_mass:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: number
|
||||||
|
active_material:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: number
|
||||||
|
capacity:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: number
|
||||||
|
battery_system:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- resource
|
||||||
|
- mount_resource
|
||||||
|
- collector_mass
|
||||||
|
- active_material
|
||||||
|
- capacity
|
||||||
|
- battery_system
|
||||||
|
_unilabos_placeholder_info:
|
||||||
|
resource: unilabos_resources
|
||||||
|
mount_resource: unilabos_resources
|
||||||
|
feedback: {}
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
goal_default:
|
||||||
|
resource: []
|
||||||
|
mount_resource: []
|
||||||
|
collector_mass: []
|
||||||
|
active_material: []
|
||||||
|
capacity: []
|
||||||
|
battery_system: []
|
||||||
|
handles:
|
||||||
|
input:
|
||||||
|
- handler_key: resource
|
||||||
|
data_type: resource
|
||||||
|
label: 待转移资源
|
||||||
|
data_key: resource
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
- handler_key: mount_resource
|
||||||
|
data_type: resource
|
||||||
|
label: 目标孔位
|
||||||
|
data_key: mount_resource
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
- handler_key: collector_mass
|
||||||
|
data_type: collector_mass
|
||||||
|
label: 极流体质量
|
||||||
|
data_key: collector_mass
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
- handler_key: active_material
|
||||||
|
data_type: active_material
|
||||||
|
label: 活性物质含量
|
||||||
|
data_key: active_material
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
- handler_key: capacity
|
||||||
|
data_type: capacity
|
||||||
|
label: 克容量
|
||||||
|
data_key: capacity
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
- handler_key: battery_system
|
||||||
|
data_type: battery_system
|
||||||
|
label: 电池体系
|
||||||
|
data_key: battery_system
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
output: []
|
||||||
|
placeholder_keys:
|
||||||
|
resource: unilabos_resources
|
||||||
|
mount_resource: unilabos_resources
|
||||||
|
feedback_interval: 1.0
|
||||||
|
transfer:
|
||||||
|
type: UniLabJsonCommandAsync
|
||||||
|
goal:
|
||||||
|
resource: resource
|
||||||
|
target_device: target_device
|
||||||
|
mount_resource: mount_resource
|
||||||
|
feedback: {}
|
||||||
|
result: {}
|
||||||
|
schema:
|
||||||
|
title: transfer参数
|
||||||
|
description: transfer的参数schema
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
goal:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
unilabos_device_id:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: UniLabOS设备ID,用于指定执行动作的具体设备实例
|
||||||
|
resource:
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
sample_id:
|
||||||
|
type: string
|
||||||
|
children:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
parent:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
category:
|
||||||
|
type: string
|
||||||
|
pose:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
position:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
x:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
y:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
z:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
required:
|
||||||
|
- x
|
||||||
|
- y
|
||||||
|
- z
|
||||||
|
title: position
|
||||||
|
additionalProperties: false
|
||||||
|
orientation:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
x:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
y:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
z:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
w:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
required:
|
||||||
|
- x
|
||||||
|
- y
|
||||||
|
- z
|
||||||
|
- w
|
||||||
|
title: orientation
|
||||||
|
additionalProperties: false
|
||||||
|
required:
|
||||||
|
- position
|
||||||
|
- orientation
|
||||||
|
title: pose
|
||||||
|
additionalProperties: false
|
||||||
|
config:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
type: string
|
||||||
|
title: resource
|
||||||
|
type: array
|
||||||
|
target_device:
|
||||||
|
type: string
|
||||||
|
description: device reference
|
||||||
|
mount_resource:
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
sample_id:
|
||||||
|
type: string
|
||||||
|
children:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
parent:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
category:
|
||||||
|
type: string
|
||||||
|
pose:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
position:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
x:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
y:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
z:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
required:
|
||||||
|
- x
|
||||||
|
- y
|
||||||
|
- z
|
||||||
|
title: position
|
||||||
|
additionalProperties: false
|
||||||
|
orientation:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
x:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
y:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
z:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
w:
|
||||||
|
type: number
|
||||||
|
minimum: -1.7976931348623157e+308
|
||||||
|
maximum: 1.7976931348623157e+308
|
||||||
|
required:
|
||||||
|
- x
|
||||||
|
- y
|
||||||
|
- z
|
||||||
|
- w
|
||||||
|
title: orientation
|
||||||
|
additionalProperties: false
|
||||||
|
required:
|
||||||
|
- position
|
||||||
|
- orientation
|
||||||
|
title: pose
|
||||||
|
additionalProperties: false
|
||||||
|
config:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
type: string
|
||||||
|
title: mount_resource
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- resource
|
||||||
|
- target_device
|
||||||
|
- mount_resource
|
||||||
|
_unilabos_placeholder_info:
|
||||||
|
resource: unilabos_resources
|
||||||
|
target_device: unilabos_devices
|
||||||
|
mount_resource: unilabos_resources
|
||||||
|
feedback: {}
|
||||||
|
result: {}
|
||||||
|
required:
|
||||||
|
- goal
|
||||||
|
goal_default:
|
||||||
|
resource: []
|
||||||
|
target_device: ''
|
||||||
|
mount_resource: []
|
||||||
|
handles:
|
||||||
|
input:
|
||||||
|
- handler_key: target_device
|
||||||
|
data_type: device_id
|
||||||
|
label: 目标设备
|
||||||
|
data_key: target_device
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
- handler_key: resource
|
||||||
|
data_type: resource
|
||||||
|
label: 待转移资源
|
||||||
|
data_key: resource
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
- handler_key: mount_resource
|
||||||
|
data_type: resource
|
||||||
|
label: 目标孔位
|
||||||
|
data_key: mount_resource
|
||||||
|
data_source: handle
|
||||||
|
io_type: source
|
||||||
|
output: []
|
||||||
|
placeholder_keys:
|
||||||
|
resource: unilabos_resources
|
||||||
|
target_device: unilabos_devices
|
||||||
|
mount_resource: unilabos_resources
|
||||||
|
feedback_interval: 1.0
|
||||||
module: unilabos.devices.neware_battery_test_system.neware_battery_test_system:NewareBatteryTestSystem
|
module: unilabos.devices.neware_battery_test_system.neware_battery_test_system:NewareBatteryTestSystem
|
||||||
status_types:
|
status_types:
|
||||||
channel_status: Dict[int, Dict]
|
channel_status: Dict[int, Dict]
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ class BatteryState(TypedDict):
|
|||||||
open_circuit_voltage: float
|
open_circuit_voltage: float
|
||||||
assembly_pressure: float
|
assembly_pressure: float
|
||||||
electrolyte_volume: float
|
electrolyte_volume: float
|
||||||
|
pole_weight: float # 极片称重 (mg)
|
||||||
|
|
||||||
info: Optional[str] # 附加信息
|
info: Optional[str] # 附加信息
|
||||||
|
|
||||||
@@ -179,6 +180,7 @@ class Battery(Container):
|
|||||||
open_circuit_voltage=0.0,
|
open_circuit_voltage=0.0,
|
||||||
assembly_pressure=0.0,
|
assembly_pressure=0.0,
|
||||||
electrolyte_volume=0.0,
|
electrolyte_volume=0.0,
|
||||||
|
pole_weight=0.0,
|
||||||
info=None
|
info=None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user