19 Commits

Author SHA1 Message Date
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
Calvin Cao
2e17dee121 Merge pull request #167 from lixinyu1011/workstation_dev_YB4
解决奔耀输入配方的,电解液体积为小数的问题
2025-11-16 17:36:50 +08:00
lixinyu1011
c03abb341a 解决奔耀输入配方的,电解液体积为小数的问题 2025-11-16 16:24:59 +08:00
calvincao
b97be6a5d4 feat(battery): 更新电池工作站配置与物料布局
- 修改弹夹尺寸默认值,确保非空时使用实际值
- 调整new_cellconfig3c.json中设备位置和尺寸配置
- 更新CoinCellDeck的尺寸和原点坐标
-重新分配所有物料和弹夹的位置坐标
- 调整电解液缓存位和回收位坐标
- 更新物料板和tip box的布局位置
2025-11-10 21:40:02 +08:00
Calvin Cao
44f830cf00 Merge pull request #163 from sun7151887/yb4-fix
更新YB_Deck堆栈坐标位置,根据图片像素坐标映射到实际尺寸
2025-11-10 19:30:26 +08:00
dijkstra402
04b578a68b 更新YB_Deck堆栈坐标位置,根据图片像素坐标映射到实际尺寸 2025-11-10 18:57:20 +08:00
calvincao
39a799cabd feat(device): 更新设备配置文件路径和图标
- 修改 bioyond_cell.yaml 中的 xlsx 文件路径为用户目录路径- 在 bioyond_cell.yaml 中新增 warehouse_name 字段并设置默认值- 为 bioyond_cell.yaml 添加 resource_tree_transfer 参数结构定义
- 更新 bioyond_cell.yaml 中的状态类型和设备 ID 配置
- 将 coin_cell_workstation.yaml 的图标从 coin_cell_assembly_picture.webp 更改为 koudian.webp
- 移除 bioyond_cell.yaml 中冗余的 display_name 配置项
2025-11-10 18:28:38 +08:00
Junhan Chang
0d64563fb6 fix serialize for magazine 2025-11-10 15:40:29 +08:00
Calvin Cao
fbb9e0963d Merge pull request #162 from sun7151887/yb4-fix
Fix import: change electrodesheet to electrode_sheet
2025-11-10 13:38:16 +08:00
dijkstra402
af411ddfe6 Fix import: change electrodesheet to electrode_sheet
修改路径
2025-11-10 13:34:49 +08:00
calvincao
f5dbcb1bfc feat(bioyond_cell): 更新默认模板路径并添加温度字段- 更新了自动送料函数中的默认 Excel 模板路径- 在物料信息中新增 temperature 字段,默认值为0
- 更新了 create_orders 函数中的默认实验文件路径
- 注释掉了部分调试代码,保留关键示例和说明
- 添加了关于位置码、实验文件和物料模板的注释提示
2025-11-10 13:27:54 +08:00
calvincao
1ecf89ea27 修改excel 2025-11-10 13:21:56 +08:00
Calvin Cao
6efdf6e5a6 Merge pull request #161 from sun7151887/yb4-fix
Fix import: change electrodesheet to electrode_sheet
2025-11-09 22:35:10 +08:00
dijkstra402
e32dc55db0 Fix import: change electrodesheet to electrode_sheet 2025-11-09 22:02:17 +08:00
Calvin Cao
acc45b716d Merge pull request #160 from sun7151887/yb4-fix
Update coin cell assembly and YB_YH materials configuration
2025-11-09 21:44:42 +08:00
dijkstra402
017eaefb8d Update coin cell assembly and YB_YH materials configuration 2025-11-09 21:43:32 +08:00
Calvin Cao
9e8c692702 Merge pull request #159 from dptech-corp/workstation_dev_YB3
Update coin cell assembly configuration: change CSV file reference an…
2025-11-09 20:57:19 +08:00
Calvin Cao
7a284069d2 Merge pull request #158 from dptech-corp/workstation_dev_YB3
Workstation dev yb3
2025-11-09 17:12:41 +08:00
Calvin Cao
a0e92b8e9b Merge pull request #156 from dptech-corp/workstation_dev_YB3
Workstation dev yb3
2025-11-09 15:48:35 +08:00
11 changed files with 327 additions and 81 deletions

View File

@@ -1,3 +1,4 @@
{ {
"nodes": [ "nodes": [
{ {
@@ -53,11 +54,6 @@
], ],
"type": "device", "type": "device",
"class":"coincellassemblyworkstation_device", "class":"coincellassemblyworkstation_device",
"position": {
"x": -600,
"y": -400,
"z": 0
},
"config": { "config": {
"deck": { "deck": {
"data": { "data": {
@@ -66,6 +62,14 @@
} }
}, },
"protocol_type": [] "protocol_type": []
},
"position": {
"size": {"height": 1450, "width": 1450, "depth": 2100},
"position": {
"x": -1500,
"y": 0,
"z": 0
}
} }
}, },
{ {
@@ -75,11 +79,6 @@
"parent": "BatteryStation", "parent": "BatteryStation",
"type": "deck", "type": "deck",
"class": "CoincellDeck", "class": "CoincellDeck",
"position": {
"x": 0,
"y": 0,
"z": 0
},
"config": { "config": {
"type": "CoincellDeck", "type": "CoincellDeck",
"setup": true, "setup": true,
@@ -95,3 +94,5 @@
], ],
"links": [] "links": []
} }

View File

@@ -11,6 +11,7 @@ from datetime import datetime, timedelta
import re import re
import threading import threading
import json import json
from copy import deepcopy
from urllib3 import response from urllib3 import response
from unilabos.devices.workstation.bioyond_studio.station import BioyondWorkstation, BioyondResourceSynchronizer from unilabos.devices.workstation.bioyond_studio.station import BioyondWorkstation, BioyondResourceSynchronizer
from unilabos.devices.workstation.bioyond_studio.config import ( from unilabos.devices.workstation.bioyond_studio.config import (
@@ -256,7 +257,7 @@ class BioyondCellWorkstation(BioyondWorkstation):
def auto_feeding4to3( def auto_feeding4to3(
self, self,
# ★ 修改点:默认模板路径 # ★ 修改点:默认模板路径
xlsx_path: Optional[str] = "/Users/sml/work/Unilab/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx", xlsx_path: Optional[str] = "/Users/calvincao/Desktop/work/uni-lab-all/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx",
# ---------------- WH4 - 加样头面 (Z=1, 12个点位) ---------------- # ---------------- WH4 - 加样头面 (Z=1, 12个点位) ----------------
WH4_x1_y1_z1_1_materialName: str = "", WH4_x1_y1_z1_1_quantity: float = 0.0, 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, WH4_x2_y1_z1_2_materialName: str = "", WH4_x2_y1_z1_2_quantity: float = 0.0,
@@ -323,6 +324,7 @@ class BioyondCellWorkstation(BioyondWorkstation):
"posX": int(row[2]), "posY": int(row[3]), "posZ": int(row[4]), "posX": int(row[2]), "posY": int(row[3]), "posZ": int(row[4]),
"materialName": str(row[5]).strip(), "materialName": str(row[5]).strip(),
"quantity": float(row[6]) if pd.notna(row[6]) else 0.0, "quantity": float(row[6]) if pd.notna(row[6]) else 0.0,
"temperature": 0,
}) })
# 四号手套箱原液瓶面 # 四号手套箱原液瓶面
for _, row in df.iloc[14:23, 2:9].iterrows(): for _, row in df.iloc[14:23, 2:9].iterrows():
@@ -334,6 +336,7 @@ class BioyondCellWorkstation(BioyondWorkstation):
"quantity": float(row[6]) if pd.notna(row[6]) else 0.0, "quantity": float(row[6]) if pd.notna(row[6]) else 0.0,
"materialType": str(row[7]).strip() if pd.notna(row[7]) else "", "materialType": str(row[7]).strip() if pd.notna(row[7]) else "",
"targetWH": str(row[8]).strip() if pd.notna(row[8]) else "", "targetWH": str(row[8]).strip() if pd.notna(row[8]) else "",
"temperature": 0,
}) })
# 三号手套箱人工堆栈 # 三号手套箱人工堆栈
for _, row in df.iloc[25:40, 2:7].iterrows(): for _, row in df.iloc[25:40, 2:7].iterrows():
@@ -343,11 +346,12 @@ class BioyondCellWorkstation(BioyondWorkstation):
"posX": int(row[2]), "posY": int(row[3]), "posZ": int(row[4]), "posX": int(row[2]), "posY": int(row[3]), "posZ": int(row[4]),
"materialType": str(row[5]).strip() if pd.notna(row[5]) else "", "materialType": str(row[5]).strip() if pd.notna(row[5]) else "",
"materialId": str(row[6]).strip() if pd.notna(row[6]) else "", "materialId": str(row[6]).strip() if pd.notna(row[6]) else "",
"quantity": 1 "quantity": 1,
"temperature": 0,
}) })
else: else:
logger.warning(f"未找到 Excel 文件 {xlsx_path},自动切换到手动参数模式。") logger.warning(f"未找到 Excel 文件 {xlsx_path},自动切换到手动参数模式。")
# TODO: 温度下面手动模式没改,上面的改了
# ---------- 模式 2: 手动填写 ---------- # ---------- 模式 2: 手动填写 ----------
if not items: if not items:
params = locals() params = locals()
@@ -473,7 +477,7 @@ class BioyondCellWorkstation(BioyondWorkstation):
- totalMass 自动计算为所有物料质量之和 - totalMass 自动计算为所有物料质量之和
- createTime 缺失或为空时自动填充为当前日期YYYY/M/D - createTime 缺失或为空时自动填充为当前日期YYYY/M/D
""" """
default_path = Path("/Users/sml/work/Unilab/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/2025092701.xlsx") default_path = Path("/Users/calvincao/Desktop/work/uni-lab-all/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/2025092701.xlsx")
path = Path(xlsx_path) if xlsx_path else default_path path = Path(xlsx_path) if xlsx_path else default_path
print(f"[create_orders] 使用 Excel 路径: {path}") print(f"[create_orders] 使用 Excel 路径: {path}")
if path != default_path: if path != default_path:
@@ -544,6 +548,14 @@ class BioyondCellWorkstation(BioyondWorkstation):
except Exception: except Exception:
return default return default
def _as_float(val, default=0.0) -> float:
try:
if pd.isna(val):
return default
return float(val)
except Exception:
return default
def _as_str(val, default="") -> str: def _as_str(val, default="") -> str:
if val is None or (isinstance(val, float) and pd.isna(val)): if val is None or (isinstance(val, float) and pd.isna(val)):
return default return default
@@ -577,9 +589,9 @@ class BioyondCellWorkstation(BioyondWorkstation):
"createTime": _to_ymd_slash(row[col_create_time]) if col_create_time else _to_ymd_slash(None), "createTime": _to_ymd_slash(row[col_create_time]) if col_create_time else _to_ymd_slash(None),
"bottleType": _as_str(row[col_bottle_type], default="配液小瓶") if col_bottle_type else "配液小瓶", "bottleType": _as_str(row[col_bottle_type], default="配液小瓶") if col_bottle_type else "配液小瓶",
"mixTime": _as_int(row[col_mix_time]) if col_mix_time else 0, "mixTime": _as_int(row[col_mix_time]) if col_mix_time else 0,
"loadSheddingInfo": _as_int(row[col_load]) if col_load else 0, "loadSheddingInfo": _as_float(row[col_load]) if col_load else 0.0,
"pouchCellInfo": _as_int(row[col_pouch]) if col_pouch else 0, "pouchCellInfo": _as_float(row[col_pouch]) if col_pouch else 0,
"conductivityInfo": _as_int(row[col_cond]) if col_cond else 0, "conductivityInfo": _as_float(row[col_cond]) if col_cond else 0,
"conductivityBottleCount": _as_int(row[col_cond_cnt]) if col_cond_cnt else 0, "conductivityBottleCount": _as_int(row[col_cond_cnt]) if col_cond_cnt else 0,
"materialInfos": mats, "materialInfos": mats,
"totalMass": round(total_mass, 4) # 自动汇总 "totalMass": round(total_mass, 4) # 自动汇总
@@ -1156,24 +1168,133 @@ class BioyondCellWorkstation(BioyondWorkstation):
if __name__ == "__main__": def run_bioyond_cell_workflow(config: Dict[str, Any]) -> BioyondCellWorkstation:
"""按照统一配置执行奔曜配液与转运工作流。
Args:
config: 统一的工作流配置。字段示例:
{
"lab_registry": {"setup": True},
"deck": {"setup": True},
"workstation": {"config": {...}},
"update_push_ip": True,
"samples": [
{"name": "...", "board_type": "...", "bottle_type": "...", "location_code": "...", "warehouse_name": "..."}
],
"scheduler": {"start": True, "log": True},
"operations": {
"auto_feeding4to3": {"enabled": True},
"create_orders": {"excel_path": "...", "log": True},
"transfer_3_to_2_to_1": {"enabled": True, "log": True},
"transfer_1_to_2": {"enabled": True, "log": True}
},
"keep_alive": False,
"keep_alive_interval": 1
}
Returns:
执行完毕的 `BioyondCellWorkstation` 实例。
"""
if config.get("lab_registry", {}).get("setup", True):
lab_registry.setup() lab_registry.setup()
deck_config = config.get("deck")
if isinstance(deck_config, dict):
deck = BIOYOND_YB_Deck(**deck_config)
elif deck_config is None:
deck = BIOYOND_YB_Deck(setup=True) deck = BIOYOND_YB_Deck(setup=True)
ws = BioyondCellWorkstation(deck=deck) else:
# ws.create_sample(name="test", board_type="配液瓶(小)板", bottle_type="配液瓶(小)", location_code="B01") deck = deck_config
# logger.info(ws.scheduler_stop())
# logger.info(ws.scheduler_start())
# 继续后续流程 workstation_kwargs = dict(config.get("workstation", {}))
# logger.info(ws.auto_feeding4to3()) #搬运物料到3号箱 if "deck" not in workstation_kwargs:
# # # 使用正斜杠或 Path 对象来指定文件路径 workstation_kwargs["deck"] = deck
# excel_path = Path("unilabos\\devices\\workstation\\bioyond_studio\\bioyond_cell\\2025092701.xlsx") ws = BioyondCellWorkstation(**workstation_kwargs)
# logger.info(ws.create_orders(excel_path))
# logger.info(ws.transfer_3_to_2_to_1())
# logger.info(ws.transfer_1_to_2()) if config.get("update_push_ip", True):
# logger.info(ws.scheduler_start()) ws.update_push_ip()
for sample_cfg in config.get("samples", []):
ws.create_sample(**sample_cfg)
scheduler_cfg = config.get("scheduler", {})
if scheduler_cfg.get("start", True):
result = ws.scheduler_start()
if scheduler_cfg.get("log", True):
logger.info(result)
operations_cfg = config.get("operations", {})
auto_feeding_cfg = operations_cfg.get("auto_feeding4to3", {})
if auto_feeding_cfg.get("enabled", True):
result = ws.auto_feeding4to3()
if auto_feeding_cfg.get("log", True):
logger.info(result)
create_orders_cfg = operations_cfg.get("create_orders")
if create_orders_cfg:
excel_path = create_orders_cfg.get("excel_path")
if not excel_path:
raise ValueError("create_orders 需要提供 excel_path。")
result = ws.create_orders(Path(excel_path))
if create_orders_cfg.get("log", True):
logger.info(result)
transfer_321_cfg = operations_cfg.get("transfer_3_to_2_to_1", {})
if transfer_321_cfg.get("enabled", True):
result = ws.transfer_3_to_2_to_1()
if transfer_321_cfg.get("log", True):
logger.info(result)
transfer_12_cfg = operations_cfg.get("transfer_1_to_2", {})
if transfer_12_cfg.get("enabled", True):
result = ws.transfer_1_to_2()
if transfer_12_cfg.get("log", True):
logger.info(result)
if config.get("keep_alive", False):
interval = config.get("keep_alive_interval", 1)
while True:
time.sleep(interval)
return ws
if __name__ == "__main__":
workflow_config = {
"deck": {"setup": True},
"update_push_ip": True,
"samples": [
{
"name": "配液瓶",
"board_type": "配液瓶(小)板",
"bottle_type": "配液瓶(小)",
"location_code": "E01",
},
{
"name": "分液瓶",
"board_type": "5ml分液瓶板",
"bottle_type": "5ml分液瓶",
"location_code": "D01",
},
],
"operations": {
"auto_feeding4to3": {"enabled": True, "log": True},
"create_orders": {
"excel_path": "/Users/calvincao/Desktop/work/uni-lab-all/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/2025092701.xlsx",
"log": True,
},
"transfer_3_to_2_to_1": {"enabled": True, "log": True},
"transfer_1_to_2": {"enabled": True, "log": True},
},
"keep_alive": True,
}
run_bioyond_cell_workflow(workflow_config)
# 1. location code
# 2. 实验文件
# 3. material template file
while True: while True:
time.sleep(1) time.sleep(1)

View File

@@ -20,7 +20,7 @@ from pylabrobot.resources.utils import create_ordered_items_2d
from unilabos.resources.battery.magazine import MagazineHolder_4_Cathode, MagazineHolder_6_Cathode, MagazineHolder_6_Anode, MagazineHolder_6_Battery from unilabos.resources.battery.magazine import MagazineHolder_4_Cathode, MagazineHolder_6_Cathode, MagazineHolder_6_Anode, MagazineHolder_6_Battery
from unilabos.resources.battery.bottle_carriers import YIHUA_Electrolyte_12VialCarrier from unilabos.resources.battery.bottle_carriers import YIHUA_Electrolyte_12VialCarrier
from unilabos.resources.battery.electrode_sheet import ElectrodeSheet
@@ -540,10 +540,10 @@ class CoincellDeck(Deck):
def __init__( def __init__(
self, self,
name: str = "coin_cell_deck", name: str = "coin_cell_deck",
size_x: float = 3650.0, # 1m size_x: float = 1450.0, # 1m
size_y: float = 1550.0, # 1m size_y: float = 1450.0, # 1m
size_z: float = 2100.0, # 0.9m size_z: float = 100.0, # 0.9m
origin: Coordinate = Coordinate(-4000, 2000, 0), origin: Coordinate = Coordinate(-2200, 0, 0),
category: str = "coin_cell_deck", category: str = "coin_cell_deck",
setup: bool = False, # 是否自动执行 setup setup: bool = False, # 是否自动执行 setup
): ):
@@ -560,11 +560,10 @@ class CoincellDeck(Deck):
""" """
super().__init__( super().__init__(
name=name, name=name,
size_x=size_x, size_x=1450.0,
size_y=size_y, size_y=1450.0,
size_z=size_z, size_z=100.0,
origin=origin, origin=origin,
category=category,
) )
if setup: if setup:
self.setup() self.setup()
@@ -575,32 +574,32 @@ class CoincellDeck(Deck):
# 正极片4个洞位2x2布局 # 正极片4个洞位2x2布局
zhengji_zip = MagazineHolder_4_Cathode("正极&铝箔弹夹") zhengji_zip = MagazineHolder_4_Cathode("正极&铝箔弹夹")
self.assign_child_resource(zhengji_zip, Coordinate(x=2799.0, y=356.0, z=0)) self.assign_child_resource(zhengji_zip, Coordinate(x=402.0, y=830.0, z=0))
# 正极壳、平垫片6个洞位2x2+2布局 # 正极壳、平垫片6个洞位2x2+2布局
zhengjike_zip = MagazineHolder_6_Cathode("正极壳&平垫片弹夹") zhengjike_zip = MagazineHolder_6_Cathode("正极壳&平垫片弹夹")
self.assign_child_resource(zhengjike_zip, Coordinate(x=2586.0, y=1143.0, z=0)) self.assign_child_resource(zhengjike_zip, Coordinate(x=566.0, y=272.0, z=0))
# 负极壳、弹垫片6个洞位2x2+2布局 # 负极壳、弹垫片6个洞位2x2+2布局
fujike_zip = MagazineHolder_6_Anode("负极壳&弹垫片弹夹") fujike_zip = MagazineHolder_6_Anode("负极壳&弹垫片弹夹")
self.assign_child_resource(fujike_zip, Coordinate(x=2492.0, y=1144.0, z=0)) self.assign_child_resource(fujike_zip, Coordinate(x=474.0, y=276.0, z=0))
# 成品弹夹6个洞位3x2布局 # 成品弹夹6个洞位3x2布局
chengpindanjia_zip = MagazineHolder_6_Battery("成品弹夹") chengpindanjia_zip = MagazineHolder_6_Battery("成品弹夹")
self.assign_child_resource(chengpindanjia_zip, Coordinate(x=3112.0, y=1295.0, z=0)) self.assign_child_resource(chengpindanjia_zip, Coordinate(x=260.0, y=156.0, z=0))
# ====================================== 物料板 ============================================ # ====================================== 物料板 ============================================
# 创建物料板料盘carrier- 4x4布局 # 创建物料板料盘carrier- 4x4布局
# 负极料盘 # 负极料盘
fujiliaopan = MaterialPlate(name="负极料盘", size_x=120, size_y=100, size_z=10.0, fill=True) fujiliaopan = MaterialPlate(name="负极料盘", size_x=120, size_y=100, size_z=10.0, fill=True)
self.assign_child_resource(fujiliaopan, Coordinate(x=2107.0, y=304.0, z=0)) self.assign_child_resource(fujiliaopan, Coordinate(x=708.0, y=794.0, z=0))
# for i in range(16): # for i in range(16):
# fujipian = ElectrodeSheet(name=f"{fujiliaopan.name}_jipian_{i}", size_x=12, size_y=12, size_z=0.1) # fujipian = ElectrodeSheet(name=f"{fujiliaopan.name}_jipian_{i}", size_x=12, size_y=12, size_z=0.1)
# fujiliaopan.children[i].assign_child_resource(fujipian, location=None) # fujiliaopan.children[i].assign_child_resource(fujipian, location=None)
# 隔膜料盘 # 隔膜料盘
gemoliaopan = MaterialPlate(name="隔膜料盘", size_x=120, size_y=100, size_z=10.0, fill=True) gemoliaopan = MaterialPlate(name="隔膜料盘", size_x=120, size_y=100, size_z=10.0, fill=True)
self.assign_child_resource(gemoliaopan, Coordinate(x=2107.0, y=146.0, z=0)) self.assign_child_resource(gemoliaopan, Coordinate(x=718.0, y=918.0, z=0))
# for i in range(16): # for i in range(16):
# gemopian = ElectrodeSheet(name=f"{gemoliaopan.name}_jipian_{i}", size_x=12, size_y=12, size_z=0.1) # gemopian = ElectrodeSheet(name=f"{gemoliaopan.name}_jipian_{i}", size_x=12, size_y=12, size_z=0.1)
# gemoliaopan.children[i].assign_child_resource(gemopian, location=None) # gemoliaopan.children[i].assign_child_resource(gemopian, location=None)
@@ -623,16 +622,16 @@ class CoincellDeck(Deck):
# 电解液缓存位 - 6x2布局 # 电解液缓存位 - 6x2布局
bottle_rack_6x2 = YIHUA_Electrolyte_12VialCarrier(name="bottle_rack_6x2") bottle_rack_6x2 = YIHUA_Electrolyte_12VialCarrier(name="bottle_rack_6x2")
self.assign_child_resource(bottle_rack_6x2, Coordinate(x=300, y=300, z=0)) self.assign_child_resource(bottle_rack_6x2, Coordinate(x=1050.0, y=358.0, z=0))
# 电解液回收位6x2 # 电解液回收位6x2
bottle_rack_6x2_2 = YIHUA_Electrolyte_12VialCarrier(name="bottle_rack_6x2_2") bottle_rack_6x2_2 = YIHUA_Electrolyte_12VialCarrier(name="bottle_rack_6x2_2")
self.assign_child_resource(bottle_rack_6x2_2, Coordinate(x=1765.0, y=869.0, z=0)) self.assign_child_resource(bottle_rack_6x2_2, Coordinate(x=914.0, y=358.0, z=0))
tip_box = TipBox64(name="tip_box_64") tip_box = TipBox64(name="tip_box_64")
self.assign_child_resource(tip_box, Coordinate(x=1938.0, y=743.0, z=0)) self.assign_child_resource(tip_box, Coordinate(x=782.0, y=514.0, z=0))
waste_tip_box = WasteTipBox(name="waste_tip_box") waste_tip_box = WasteTipBox(name="waste_tip_box")
self.assign_child_resource(waste_tip_box, Coordinate(x=1960.0, y=639.0, z=0)) self.assign_child_resource(waste_tip_box, Coordinate(x=778.0, y=622.0, z=0))
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -139,7 +139,7 @@ class CoinCellAssemblyWorkstation(WorkstationBase):
time.sleep(2) time.sleep(2)
if not modbus_client.client.is_socket_open(): if not modbus_client.client.is_socket_open():
raise ValueError('modbus tcp connection failed') raise ValueError('modbus tcp connection failed')
self.nodes = BaseClient.load_csv(os.path.join(os.path.dirname(__file__), 'coin_cell_assembly_1105.csv')) self.nodes = BaseClient.load_csv(os.path.join(os.path.dirname(__file__), 'coin_cell_assembly_a.csv'))
self.client = modbus_client.register_node_list(self.nodes) self.client = modbus_client.register_node_list(self.nodes)
else: else:
print("测试模式,跳过连接") print("测试模式,跳过连接")
@@ -791,7 +791,7 @@ class CoinCellAssemblyWorkstation(WorkstationBase):
logger.debug(f"data_electrolyte_code: {data_electrolyte_code}") logger.debug(f"data_electrolyte_code: {data_electrolyte_code}")
logger.debug(f"data_coin_cell_code: {data_coin_cell_code}") logger.debug(f"data_coin_cell_code: {data_coin_cell_code}")
#接收完信息后读取完毕标志位置True #接收完信息后读取完毕标志位置True
liaopan3 = self.deck.get_resource("chengpindanjia") liaopan3 = self.deck.get_resource("成品弹夹")
#把物料解绑后放到另一盘上 #把物料解绑后放到另一盘上
battery = ElectrodeSheet(name=f"battery_{self.coin_num_N}", size_x=14, size_y=14, size_z=2) battery = ElectrodeSheet(name=f"battery_{self.coin_num_N}", size_x=14, size_y=14, size_z=2)
battery._unilabos_state = { battery._unilabos_state = {
@@ -1008,7 +1008,7 @@ class CoinCellAssemblyWorkstation(WorkstationBase):
# time.sleep(1) # time.sleep(1)
# time.sleep(40) # time.sleep(40)
# 数据读取与输出 # 数据读取与输出
def func_read_data_and_output(self, file_path: str="D:\\coin_cell_data"): def func_read_data_and_output(self, file_path: str="/Users/sml/work"):
# 检查CSV导出是否正在运行已运行则跳出防止同时启动两个while循环 # 检查CSV导出是否正在运行已运行则跳出防止同时启动两个while循环
if self.csv_export_running: if self.csv_export_running:
return False, "读取已在运行中" return False, "读取已在运行中"
@@ -1202,17 +1202,93 @@ class CoinCellAssemblyWorkstation(WorkstationBase):
''' '''
def run_coin_cell_packaging_workflow(config: Dict[str, Any]) -> CoinCellAssemblyWorkstation:
"""根据统一配置顺序执行扣电池装配工作流。
Args:
config: 统一的工作流配置。字段示例:
{
"deck": {"setup": True, "name": "coin_cell_deck"},
"workstation": {"address": "...", "port": "...", "debug_mode": False},
"qiming": {...},
"init": True,
"auto": True,
"start": True,
"packaging": {
"bottle_num": 16,
"command": {...}
}
}
Returns:
执行完毕的 `CoinCellAssemblyWorkstation` 实例。
"""
deck_config = config.get("deck")
if isinstance(deck_config, Deck):
deck = deck_config
elif isinstance(deck_config, dict):
deck = CoincellDeck(**deck_config)
elif deck_config is None:
deck = CoincellDeck(setup=True, name="coin_cell_deck")
else:
raise ValueError("deck 配置需为 Deck 实例或 dict。")
workstation_config = dict(config.get("workstation", {}))
workstation_config.setdefault("deck", deck)
workstation = CoinCellAssemblyWorkstation(**workstation_config)
qiming_params = config.get("qiming", {})
if qiming_params:
workstation.qiming_coin_cell_code(**qiming_params)
if config.get("init", True):
workstation.func_pack_device_init()
if config.get("auto", True):
workstation.func_pack_device_auto()
if config.get("start", True):
workstation.func_pack_device_start()
packaging_config = config.get("packaging", {})
bottle_num = packaging_config.get("bottle_num")
if bottle_num is not None:
workstation.func_pack_send_bottle_num(bottle_num)
allpack_params = packaging_config.get("command", {})
if allpack_params:
workstation.func_allpack_cmd(**allpack_params)
return workstation
if __name__ == "__main__": if __name__ == "__main__":
# 简单测试 workflow_config = {
workstation = CoinCellAssemblyWorkstation(deck=CoincellDeck(setup=True, name="coin_cell_deck")) "deck": {"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) "workstation": {
# print(f"工作站创建成功: {workstation.deck.name}") "address": "172.16.28.102",
# print(f"料盘数量: {len(workstation.deck.children)}") "port": "502",
workstation.func_pack_device_init() "debug_mode": False,
workstation.func_pack_device_auto() },
workstation.func_pack_device_start() "qiming": {
workstation.func_pack_send_bottle_num(16) "fujipian_panshu": 1,
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") "fujipian_juzhendianwei": 2,
"gemopanshu": 3,
"gemo_juzhendianwei": 4,
"lvbodian": False,
"battery_pressure_mode": False,
"battery_pressure": 4200,
"battery_clean_ignore": False,
},
"packaging": {
"bottle_num": 16,
"command": {
"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",
},
},
}
run_coin_cell_packaging_workflow(workflow_config)

View File

@@ -4,7 +4,6 @@ bioyond_cell:
class: class:
action_value_mappings: action_value_mappings:
auto-auto_batch_outbound_from_xlsx: auto-auto_batch_outbound_from_xlsx:
display_name: 批量导入上料
feedback: {} feedback: {}
goal: {} goal: {}
goal_default: goal_default:
@@ -138,7 +137,7 @@ bioyond_cell:
WH4_x5_y1_z1_5_quantity: 0.0 WH4_x5_y1_z1_5_quantity: 0.0
WH4_x5_y2_z1_10_materialName: '' WH4_x5_y2_z1_10_materialName: ''
WH4_x5_y2_z1_10_quantity: 0.0 WH4_x5_y2_z1_10_quantity: 0.0
xlsx_path: C:/ML/GitHub/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx xlsx_path: /Users/sml/work/Unilab/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx
handles: {} handles: {}
placeholder_keys: {} placeholder_keys: {}
result: {} result: {}
@@ -464,7 +463,7 @@ bioyond_cell:
default: 0.0 default: 0.0
type: number type: number
xlsx_path: xlsx_path:
default: C:/ML/GitHub/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx default: /Users/sml/work/Unilab/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx
type: string type: string
required: [] required: []
type: object type: object
@@ -600,6 +599,7 @@ bioyond_cell:
bottle_type: null bottle_type: null
location_code: null location_code: null
name: null name: null
warehouse_name: 手动堆栈
handles: {} handles: {}
placeholder_keys: {} placeholder_keys: {}
result: {} result: {}
@@ -617,6 +617,9 @@ bioyond_cell:
type: string type: string
name: name:
type: string type: string
warehouse_name:
default: 手动堆栈
type: string
required: required:
- name - name
- board_type - board_type
@@ -785,6 +788,39 @@ bioyond_cell:
title: report_material_change参数 title: report_material_change参数
type: object type: object
type: UniLabJsonCommand type: UniLabJsonCommand
auto-resource_tree_transfer:
feedback: {}
goal: {}
goal_default:
old_parent: null
parent_resource: null
plr_resource: null
handles: {}
placeholder_keys: {}
result: {}
schema:
description: ''
properties:
feedback: {}
goal:
properties:
old_parent:
type: object
parent_resource:
type: object
plr_resource:
type: object
required:
- old_parent
- plr_resource
- parent_resource
type: object
result: {}
required:
- goal
title: resource_tree_transfer参数
type: object
type: UniLabJsonCommand
auto-scheduler_continue: auto-scheduler_continue:
feedback: {} feedback: {}
goal: {} goal: {}
@@ -1072,12 +1108,13 @@ bioyond_cell:
type: object type: object
type: UniLabJsonCommand type: UniLabJsonCommand
module: unilabos.devices.workstation.bioyond_studio.bioyond_cell.bioyond_cell_workstation:BioyondCellWorkstation module: unilabos.devices.workstation.bioyond_studio.bioyond_cell.bioyond_cell_workstation:BioyondCellWorkstation
status_types: {} status_types:
device_id: String
type: python type: python
config_info: [] config_info: []
description: '' description: ''
handles: [] handles: []
icon: '' icon: benyao2.webp
init_param_schema: init_param_schema:
config: config:
properties: properties:
@@ -1090,8 +1127,11 @@ bioyond_cell:
required: [] required: []
type: object type: object
data: data:
properties: {} properties:
required: [] device_id:
type: string
required:
- device_id
type: object type: object
registry_type: device registry_type: device
version: 1.0.0 version: 1.0.0

View File

@@ -502,7 +502,7 @@ coincellassemblyworkstation_device:
config_info: [] config_info: []
description: '' description: ''
handles: [] handles: []
icon: coin_cell_assembly_picture.webp icon: koudian.webp
init_param_schema: init_param_schema:
config: config:
properties: properties:

View File

View File

@@ -52,6 +52,15 @@ class Magazine(ResourceStack):
def size_z(self) -> float: def size_z(self) -> float:
return self.get_size_z() return self.get_size_z()
def serialize(self) -> dict:
return {
**super().serialize(),
"size_x": self.size_x or 10.0,
"size_y": self.size_y or 10.0,
"size_z": self.size_z or 10.0,
"max_sheets": self.max_sheets,
}
class MagazineHolder(ItemizedResource): class MagazineHolder(ItemizedResource):
"""子弹夹类 - 有多个洞位,每个洞位放多个极片""" """子弹夹类 - 有多个洞位,每个洞位放多个极片"""

View File

@@ -95,13 +95,13 @@ class BIOYOND_YB_Deck(Deck):
} }
# warehouse 的位置 # warehouse 的位置
self.warehouse_locations = { self.warehouse_locations = {
"自动堆栈-左": Coordinate(-300.0, 158.0, 0.0), "自动堆栈-左": Coordinate(-100.3, 171.5, 0.0),
"自动堆栈-右": Coordinate(4160.0, 158.0, 0.0), "自动堆栈-右": Coordinate(3960.1, 155.9, 0.0),
"手动堆栈-左": Coordinate(-400.0, 877.0, 0.0), "手动堆栈-左": Coordinate(-213.3, 804.4, 0.0),
"手动堆栈-右": Coordinate(4160.0, 877.0, 0.0), "手动堆栈-右": Coordinate(3960.1, 807.6, 0.0),
"粉末加样头堆栈": Coordinate(415.0, 1301.0, 0.0), "粉末加样头堆栈": Coordinate(415.0, 1301.0, 0.0),
"配液站内试剂仓库": Coordinate(2162.0, 337.0, 0.0), "配液站内试剂仓库": Coordinate(2162.0, 437.0, 0.0),
"试剂替换仓库": Coordinate(1173.0, 702.0, 0.0), "试剂替换仓库": Coordinate(1173.0, 802.0, 0.0),
} }
for warehouse_name, warehouse in self.warehouses.items(): for warehouse_name, warehouse in self.warehouses.items():