add electrode_sheets definition, and fix magazines

This commit is contained in:
Junhan Chang
2025-11-09 01:00:05 +08:00
parent 9bba4620b7
commit f872d3ef56
3 changed files with 354 additions and 216 deletions

View File

@@ -18,70 +18,11 @@ from pylabrobot.resources.tip_rack import TipRack, TipSpot
from pylabrobot.resources.trash import Trash from pylabrobot.resources.trash import Trash
from pylabrobot.resources.utils import create_ordered_items_2d from pylabrobot.resources.utils import create_ordered_items_2d
from unilabos.resources.battery.magazine import MagazineHolder_1, MagazineHolder_2, MagazineHolder_4, MagazineHolder_6 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
class ElectrodeSheetState(TypedDict):
diameter: float # 直径 (mm)
thickness: float # 厚度 (mm)
mass: float # 质量 (g)
material_type: str # 材料类型(正极、负极、隔膜、弹片、垫片、铝箔等)
height: float
electrolyte_name: str
data_electrolyte_code: str
open_circuit_voltage: float
assembly_pressure: float
electrolyte_volume: float
info: Optional[str] # 附加信息
class ElectrodeSheet(Resource):
"""极片类 - 包含正负极片、隔膜、弹片、垫片、铝箔等所有片状材料"""
def __init__(
self,
name: str = "极片",
size_x=10,
size_y=10,
size_z=10,
category: str = "electrode_sheet",
model: Optional[str] = None,
):
"""初始化极片
Args:
name: 极片名称
category: 类别
model: 型号
"""
super().__init__(
name=name,
size_x=size_x,
size_y=size_y,
size_z=size_z,
category=category,
model=model,
)
self._unilabos_state: ElectrodeSheetState = ElectrodeSheetState(
diameter=14,
thickness=0.1,
mass=0.5,
material_type="copper",
info=None
)
# TODO: 这个还要不要给self._unilabos_state赋值的
def load_state(self, state: Dict[str, Any]) -> None:
"""格式不变"""
super().load_state(state)
self._unilabos_state = state
#序列化
def serialize_state(self) -> Dict[str, Dict[str, Any]]:
"""格式不变"""
data = super().serialize_state()
data.update(self._unilabos_state) # Container自身的信息云端物料将保存这一data本地也通过这里的data进行读写当前类用来表示这个物料的长宽高大小的属性而datastate用来表示物料的内容细节等
return data
# TODO: 这个应该只能放一个极片 # TODO: 这个应该只能放一个极片
class MaterialHoleState(TypedDict): class MaterialHoleState(TypedDict):
@@ -477,13 +418,13 @@ def TipBox64(
size_x: float = 127.8, size_x: float = 127.8,
size_y: float = 85.5, size_y: float = 85.5,
size_z: float = 60.0, size_z: float = 60.0,
category: str = "tip_box_64", category: str = "tip_rack",
model: Optional[str] = None, model: Optional[str] = None,
): ):
"""64孔枪头盒类""" """64孔枪头盒类"""
from pylabrobot.resources.tip import Tip from pylabrobot.resources.tip import Tip
# 创建8x8=64个枪头位 # 创建12x8=96个枪头位
def make_tip(): def make_tip():
return Tip( return Tip(
has_filter=False, has_filter=False,
@@ -508,17 +449,19 @@ def TipBox64(
) )
idx_available = list(range(0, 32)) + list(range(64, 96)) idx_available = list(range(0, 32)) + list(range(64, 96))
tip_spots_available = {k: v for i, (k, v) in enumerate(tip_spots.items()) if i in idx_available} tip_spots_available = {k: v for i, (k, v) in enumerate(tip_spots.items()) if i in idx_available}
return TipRack( tip_rack = TipRack(
name=name, name=name,
size_x=size_x, size_x=size_x,
size_y=size_y, size_y=size_y,
size_z=size_z, size_z=size_z,
ordered_items=tip_spots_available, # ordered_items=tip_spots_available,
ordered_items=tip_spots,
category=category, category=category,
model=model, model=model,
with_tips=True, with_tips=False,
) )
tip_rack.set_tip_state([True]*32 + [False]*32 + [True]*32) # 前32和后32个有枪头中间32个无枪头
return tip_rack
class WasteTipBoxstate(TypedDict): class WasteTipBoxstate(TypedDict):
@@ -629,58 +572,23 @@ class CoincellDeck(Deck):
def setup(self) -> None: def setup(self) -> None:
"""设置工作站的标准布局 - 包含子弹夹、料盘、瓶架等完整配置""" """设置工作站的标准布局 - 包含子弹夹、料盘、瓶架等完整配置"""
# ====================================== 子弹夹 ============================================ # ====================================== 子弹夹 ============================================
# 铝箔1个洞位
lvbo_zip = MagazineHolder_1("铝箔弹夹", 80, 80, 10)
self.assign_child_resource(lvbo_zip, Coordinate(x=2737.0, y=301.0, z=0))
# 正极片4个洞位2x2布局 # 正极片4个洞位2x2布局
zhengji_zip = MagazineHolder_4("正极弹夹", 80, 80, 10) 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=2799.0, y=356.0, z=0))
# 正极壳4个洞位2x2布局 # 正极壳、平垫片6个洞位2x2+2布局)
zhengjike_zip = MagazineHolder_4("正极壳弹夹", 80, 80, 10) 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=2586.0, y=1143.0, z=0))
# 垫片(2个洞位,1x2布局 # 负极壳、弹垫片(6个洞位,2x2+2布局
danpian_zip = MagazineHolder_2("垫片弹夹", 80, 80, 10) fujike_zip = MagazineHolder_6_Anode("负极壳&弹垫片弹夹")
self.assign_child_resource(danpian_zip, Coordinate(x=2690.0, y=1141.0, z=0))
# 负极壳4个洞位2x2布局
fujike_zip = MagazineHolder_4("负极壳弹夹", 80, 80, 10)
self.assign_child_resource(fujike_zip, Coordinate(x=2492.0, y=1144.0, z=0)) self.assign_child_resource(fujike_zip, Coordinate(x=2492.0, y=1144.0, z=0))
# 弹片2个洞位1x2布局
tanpian_zip = MagazineHolder_2("弹片弹夹", 80, 80, 10)
self.assign_child_resource(tanpian_zip, Coordinate(x=2492.0, y=1139.0, z=0))
# 成品弹夹6个洞位3x2布局 # 成品弹夹6个洞位3x2布局
chengpindanjia_zip = MagazineHolder_6("成品弹夹", 80, 80, 10) 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=3112.0, y=1295.0, z=0))
# 为子弹夹添加极片
for i in range(1): # MagazineHolder_1 有1个洞位
lvbo = ElectrodeSheet(name=f"铝箔_{i}", size_x=12, size_y=12, size_z=0.1)
lvbo_zip.children[i].assign_child_resource(lvbo, location=None)
for i in range(4): # MagazineHolder_4 有4个洞位
zhengji = ElectrodeSheet(name=f"正极_{i}", size_x=12, size_y=12, size_z=0.1)
zhengji_zip.children[i].assign_child_resource(zhengji, location=None)
for i in range(4): # MagazineHolder_4 有4个洞位
zhengjike = ElectrodeSheet(name=f"正极壳_{i}", size_x=12, size_y=12, size_z=0.1)
zhengjike_zip.children[i].assign_child_resource(zhengjike, location=None)
for i in range(2): # MagazineHolder_2 有2个洞位
danpian = ElectrodeSheet(name=f"垫片_{i}", size_x=12, size_y=12, size_z=0.1)
danpian_zip.children[i].assign_child_resource(danpian, location=None)
for i in range(4): # MagazineHolder_4 有4个洞位
fujike = ElectrodeSheet(name=f"负极壳_{i}", size_x=12, size_y=12, size_z=0.1)
fujike_zip.children[i].assign_child_resource(fujike, location=None)
for i in range(2): # MagazineHolder_2 有2个洞位
tanpian = ElectrodeSheet(name=f"弹片_{i}", size_x=12, size_y=12, size_z=0.1)
tanpian_zip.children[i].assign_child_resource(tanpian, location=None)
# for i in range(6): # MagazineHolder_6 有6个洞位
# chengpindanjia = ElectrodeSheet(name=f"成品弹夹_{i}", size_x=12, size_y=12, size_z=0.1)
# chengpindanjia_zip.children[i].assign_child_resource(chengpindanjia, location=None)
# ====================================== 物料板 ============================================ # ====================================== 物料板 ============================================
# 创建物料板料盘carrier- 4x4布局 # 创建物料板料盘carrier- 4x4布局
# 负极料盘 # 负极料盘
@@ -699,7 +607,7 @@ class CoincellDeck(Deck):
# ====================================== 瓶架、移液枪 ============================================ # ====================================== 瓶架、移液枪 ============================================
# 在台面上放置 3x4 瓶架、6x2 瓶架 与 64孔移液枪头盒 # 在台面上放置 3x4 瓶架、6x2 瓶架 与 64孔移液枪头盒
# 奔耀上料5ml分液瓶小板 - 由奔曜跨站转运而来,不单独写 # 奔耀上料5ml分液瓶小板 - 由奔曜跨站转运而来,不单独写,但是这里应该有一个堆栈用于摆放分液瓶小板
# bottle_rack_3x4 = BottleRack( # bottle_rack_3x4 = BottleRack(
# name="bottle_rack_3x4", # name="bottle_rack_3x4",

View File

@@ -0,0 +1,179 @@
from typing import Any, Dict, Optional, TypedDict
from pylabrobot.resources import Resource as ResourcePLR
from pylabrobot.resources import Container
electrode_colors = {
"PositiveCan": "#ff0000",
"PositiveElectrode": "#cc3333",
"NegativeCan": "#000000",
"NegativeElectrode": "#666666",
"SpringWasher": "#8b7355",
"FlatWasher": "a9a9a9",
"AluminumFoil": "#ffcccc",
"Battery": "#00ff00",
}
class ElectrodeSheetState(TypedDict):
mass: float # 质量 (g)
material_type: str # 材料类型(铜、铝、不锈钢、弹簧钢等)
color: str # 材料类型对应的颜色
class ElectrodeSheet(ResourcePLR):
"""极片类 - 包含正负极片、隔膜、弹片、垫片、铝箔等所有片状材料"""
def __init__(
self,
name: str = "极片",
size_x=10,
size_y=10,
size_z=10,
category: str = "electrode_sheet",
model: Optional[str] = None,
):
"""初始化极片
Args:
name: 极片名称
category: 类别
model: 型号
"""
super().__init__(
name=name,
size_x=size_x,
size_y=size_y,
size_z=size_z,
category=category,
model=model,
)
self._unilabos_state: ElectrodeSheetState = ElectrodeSheetState(
diameter=14,
thickness=0.1,
mass=0.5,
material_type="copper",
info=None
)
# TODO: 这个还要不要给self._unilabos_state赋值的
def load_state(self, state: Dict[str, Any]) -> None:
"""格式不变"""
super().load_state(state)
self._unilabos_state = state
#序列化
def serialize_state(self) -> Dict[str, Dict[str, Any]]:
"""格式不变"""
data = super().serialize_state()
data.update(self._unilabos_state) # Container自身的信息云端物料将保存这一data本地也通过这里的data进行读写当前类用来表示这个物料的长宽高大小的属性而datastate用来表示物料的内容细节等
return data
def PositiveCan(name: str) -> ElectrodeSheet:
"""创建正极壳"""
sheet = ElectrodeSheet(name=name, size_x=12, size_y=12, size_z=3.0, model="PositiveCan")
sheet.load_state({"material_type": "aluminum", "color": electrode_colors["PositiveCan"]})
return sheet
def PositiveElectrode(name: str) -> ElectrodeSheet:
"""创建正极片"""
sheet = ElectrodeSheet(name=name, size_x=10, size_y=10, size_z=0.1, model="PositiveElectrode")
sheet.load_state({"material_type": "positive_electrode", "color": electrode_colors["PositiveElectrode"]})
return sheet
def NegativeCan(name: str) -> ElectrodeSheet:
"""创建负极壳"""
sheet = ElectrodeSheet(name=name, size_x=12, size_y=12, size_z=2.0, model="NegativeCan")
sheet.load_state({"material_type": "steel", "color": electrode_colors["NegativeCan"]})
return sheet
def NegativeElectrode(name: str) -> ElectrodeSheet:
"""创建负极片"""
sheet = ElectrodeSheet(name=name, size_x=10, size_y=10, size_z=0.1, model="NegativeElectrode")
sheet.load_state({"material_type": "negative_electrode", "color": electrode_colors["NegativeElectrode"]})
return sheet
def SpringWasher(name: str) -> ElectrodeSheet:
"""创建弹片"""
sheet = ElectrodeSheet(name=name, size_x=10, size_y=10, size_z=0.5, model="SpringWasher")
sheet.load_state({"material_type": "spring_steel", "color": electrode_colors["SpringWasher"]})
return sheet
def FlatWasher(name: str) -> ElectrodeSheet:
"""创建垫片"""
sheet = ElectrodeSheet(name=name, size_x=10, size_y=10, size_z=0.2, model="FlatWasher")
sheet.load_state({"material_type": "steel", "color": electrode_colors["FlatWasher"]})
return sheet
def AluminumFoil(name: str) -> ElectrodeSheet:
"""创建铝箔"""
sheet = ElectrodeSheet(name=name, size_x=10, size_y=10, size_z=0.05, model="AluminumFoil")
sheet.load_state({"material_type": "aluminum", "color": electrode_colors["AluminumFoil"]})
return sheet
class BatteryState(TypedDict):
color: str # 材料类型对应的颜色
electrolyte_name: str
data_electrolyte_code: str
open_circuit_voltage: float
assembly_pressure: float
electrolyte_volume: float
info: Optional[str] # 附加信息
class Battery(Container):
"""电池类 - 包含组装好的电池"""
def __init__(
self,
name: str = "电池",
size_x=12,
size_y=12,
size_z=6,
category: str = "battery",
model: Optional[str] = None,
):
"""初始化电池
Args:
name: 电池名称
category: 类别
model: 型号
"""
super().__init__(
name=name,
size_x=size_x,
size_y=size_y,
size_z=size_z,
category=category,
model=model,
)
self._unilabos_state: BatteryState = BatteryState(
color=electrode_colors["Battery"],
electrolyte_name="",
data_electrolyte_code="",
open_circuit_voltage=0.0,
assembly_pressure=0.0,
electrolyte_volume=0.0,
info=None
)
def load_state(self, state: Dict[str, Any]) -> None:
"""格式不变"""
super().load_state(state)
self._unilabos_state = state
#序列化
def serialize_state(self) -> Dict[str, Dict[str, Any]]:
"""格式不变"""
data = super().serialize_state()
data.update(self._unilabos_state) # Container自身的信息云端物料将保存这一data本地也通过这里的data进行读写当前类用来表示这个物料的长宽高大小的属性而datastate用来表示物料的内容细节等
return data

View File

@@ -1,10 +1,18 @@
from typing import Dict, List, Optional, OrderedDict, Union from typing import Dict, List, Optional, OrderedDict, Union, Callable
import math import math
from pylabrobot.resources.coordinate import Coordinate from pylabrobot.resources.coordinate import Coordinate
from pylabrobot.resources import Resource, ResourceStack, ItemizedResource from pylabrobot.resources import Resource, ResourceStack, ItemizedResource
from pylabrobot.resources.carrier import create_homogeneous_resources from pylabrobot.resources.carrier import create_homogeneous_resources
from unilabos.resources.battery.electrode_sheet import (
PositiveCan, PositiveElectrode,
NegativeCan, NegativeElectrode,
SpringWasher, FlatWasher,
AluminumFoil,
Battery
)
class Magazine(ResourceStack): class Magazine(ResourceStack):
"""子弹夹洞位类""" """子弹夹洞位类"""
@@ -32,6 +40,18 @@ class Magazine(ResourceStack):
) )
self.max_sheets = max_sheets self.max_sheets = max_sheets
@property
def size_x(self) -> float:
return self.get_size_x()
@property
def size_y(self) -> float:
return self.get_size_y()
@property
def size_z(self) -> float:
return self.get_size_z()
class MagazineHolder(ItemizedResource): class MagazineHolder(ItemizedResource):
"""子弹夹类 - 有多个洞位,每个洞位放多个极片""" """子弹夹类 - 有多个洞位,每个洞位放多个极片"""
@@ -98,6 +118,7 @@ def magazine_factory(
size_y: float, size_y: float,
size_z: float, size_z: float,
locations: List[Coordinate], locations: List[Coordinate],
klasses: Optional[List[Callable[[str], str]]] = None,
hole_diameter: float = 14.0, hole_diameter: float = 14.0,
hole_depth: float = 10.0, hole_depth: float = 10.0,
max_sheets_per_hole: int = 100, max_sheets_per_hole: int = 100,
@@ -112,12 +133,17 @@ def magazine_factory(
size_y: 宽度 (mm) size_y: 宽度 (mm)
size_z: 高度 (mm) size_z: 高度 (mm)
locations: 洞位坐标列表 locations: 洞位坐标列表
klasses: 每个洞位中极片的类列表
hole_diameter: 洞直径 (mm) hole_diameter: 洞直径 (mm)
hole_depth: 洞深度 (mm) hole_depth: 洞深度 (mm)
max_sheets_per_hole: 每个洞位最大极片数量 max_sheets_per_hole: 每个洞位最大极片数量
category: 类别 category: 类别
model: 型号 model: 型号
""" """
for loc in locations:
loc.x -= hole_diameter / 2
loc.y -= hole_diameter / 2
# 创建洞位 # 创建洞位
_sites = create_homogeneous_resources( _sites = create_homogeneous_resources(
klass=Magazine, klass=Magazine,
@@ -132,7 +158,7 @@ def magazine_factory(
keys = [f"A{i+1}" for i in range(len(locations))] keys = [f"A{i+1}" for i in range(len(locations))]
sites = dict(zip(keys, _sites.values())) sites = dict(zip(keys, _sites.values()))
return MagazineHolder( holder = MagazineHolder(
name=name, name=name,
size_x=size_x, size_x=size_x,
size_y=size_y, size_y=size_y,
@@ -145,18 +171,143 @@ def magazine_factory(
model=model, model=model,
) )
if klasses is not None:
for i, klass in enumerate(klasses):
hole_key = keys[i]
hole = holder.children[i]
for j in reversed(range(max_sheets_per_hole)):
item_name = f"{hole_key}_sheet{j+1}"
item = klass(name=item_name)
hole.assign_child_resource(item)
return holder
def MagazineHolder_4(
def MagazineHolder_6_Cathode(
name: str, name: str,
size_x: float = 80.0, size_x: float = 80.0,
size_y: float = 80.0, size_y: float = 80.0,
size_z: float = 10.0, size_z: float = 40.0,
hole_diameter: float = 14.0, hole_diameter: float = 14.0,
hole_depth: float = 10.0, hole_depth: float = 10.0,
hole_spacing: float = 25.0, hole_spacing: float = 20.0,
max_sheets_per_hole: int = 100, max_sheets_per_hole: int = 100,
) -> MagazineHolder:
"""创建6孔子弹夹 - 六边形排布"""
center_x = size_x / 2
center_y = size_y / 2
locations = []
# 周围6个孔按六边形排布
for i in range(6):
angle = i * 60 * math.pi / 180 # 每60度一个孔
x = center_x + hole_spacing * math.cos(angle)
y = center_y + hole_spacing * math.sin(angle)
locations.append(Coordinate(x, y, size_z - hole_depth))
return magazine_factory(
name=name,
size_x=size_x,
size_y=size_y,
size_z=size_z,
locations=locations,
klasses=[FlatWasher, PositiveCan, PositiveCan, FlatWasher, PositiveCan, PositiveCan],
hole_diameter=hole_diameter,
hole_depth=hole_depth,
max_sheets_per_hole=max_sheets_per_hole,
category="magazine_holder",
model="MagazineHolder_6_Cathode",
)
def MagazineHolder_6_Anode(
name: str,
size_x: float = 80.0,
size_y: float = 80.0,
size_z: float = 40.0,
hole_diameter: float = 14.0,
hole_depth: float = 10.0,
hole_spacing: float = 20.0,
max_sheets_per_hole: int = 100,
) -> MagazineHolder:
"""创建6孔子弹夹 - 六边形排布"""
center_x = size_x / 2
center_y = size_y / 2
locations = []
# 周围6个孔按六边形排布
for i in range(6):
angle = i * 60 * math.pi / 180 # 每60度一个孔
x = center_x + hole_spacing * math.cos(angle)
y = center_y + hole_spacing * math.sin(angle)
locations.append(Coordinate(x, y, size_z - hole_depth))
return magazine_factory(
name=name,
size_x=size_x,
size_y=size_y,
size_z=size_z,
locations=locations,
klasses=[SpringWasher, NegativeCan, NegativeCan, SpringWasher, NegativeCan, NegativeCan],
hole_diameter=hole_diameter,
hole_depth=hole_depth,
max_sheets_per_hole=max_sheets_per_hole,
category="magazine_holder",
model="MagazineHolder_6_Anode",
)
def MagazineHolder_6_Battery(
name: str,
size_x: float = 80.0,
size_y: float = 80.0,
size_z: float = 40.0,
hole_diameter: float = 14.0,
hole_depth: float = 10.0,
hole_spacing: float = 20.0,
max_sheets_per_hole: int = 100,
) -> MagazineHolder:
"""创建6孔子弹夹 - 六边形排布"""
center_x = size_x / 2
center_y = size_y / 2
locations = []
# 周围6个孔按六边形排布
for i in range(6):
angle = i * 60 * math.pi / 180 # 每60度一个孔
x = center_x + hole_spacing * math.cos(angle)
y = center_y + hole_spacing * math.sin(angle)
locations.append(Coordinate(x, y, size_z - hole_depth))
return magazine_factory(
name=name,
size_x=size_x,
size_y=size_y,
size_z=size_z,
locations=locations,
klasses=None, # 初始化时,不放入装好的电池
hole_diameter=hole_diameter,
hole_depth=hole_depth,
max_sheets_per_hole=max_sheets_per_hole,
category="magazine_holder",
model="MagazineHolder_6_Battery",
)
def MagazineHolder_4_Cathode(
name: str,
) -> MagazineHolder: ) -> MagazineHolder:
"""创建4孔子弹夹 - 正方形四角排布""" """创建4孔子弹夹 - 正方形四角排布"""
size_x: float = 80.0
size_y: float = 80.0
size_z: float = 10.0
hole_diameter: float = 14.0
hole_depth: float = 10.0
hole_spacing: float = 25.0
max_sheets_per_hole: int = 100
# 计算4个洞位的坐标正方形四角排布 # 计算4个洞位的坐标正方形四角排布
center_x = size_x / 2 center_x = size_x / 2
center_y = size_y / 2 center_y = size_y / 2
@@ -175,110 +326,10 @@ def MagazineHolder_4(
size_y=size_y, size_y=size_y,
size_z=size_z, size_z=size_z,
locations=locations, locations=locations,
klasses=[AluminumFoil, PositiveElectrode, PositiveElectrode, PositiveElectrode],
hole_diameter=hole_diameter, hole_diameter=hole_diameter,
hole_depth=hole_depth, hole_depth=hole_depth,
max_sheets_per_hole=max_sheets_per_hole, max_sheets_per_hole=max_sheets_per_hole,
category="clip_magazine_four", category="magazine_holder",
model="MagazineHolder_4_Cathode",
) )
def MagazineHolder_2(
name: str,
size_x: float = 80.0,
size_y: float = 80.0,
size_z: float = 10.0,
hole_diameter: float = 14.0,
hole_depth: float = 10.0,
hole_spacing: float = 25.0,
max_sheets_per_hole: int = 100,
) -> MagazineHolder:
"""创建2孔子弹夹 - 竖向排布"""
# 计算2个洞位的坐标竖向排布
center_x = size_x / 2
center_y = size_y / 2
offset = hole_spacing / 2
locations = [
Coordinate(center_x, center_y - offset, size_z - hole_depth), # 下方
Coordinate(center_x, center_y + offset, size_z - hole_depth), # 上方
]
return magazine_factory(
name=name,
size_x=size_x,
size_y=size_y,
size_z=size_z,
locations=locations,
hole_diameter=hole_diameter,
hole_depth=hole_depth,
max_sheets_per_hole=max_sheets_per_hole,
category="clip_magazine_two",
)
def MagazineHolder_1(
name: str,
size_x: float = 80.0,
size_y: float = 80.0,
size_z: float = 10.0,
hole_diameter: float = 14.0,
hole_depth: float = 10.0,
max_sheets_per_hole: int = 100,
) -> MagazineHolder:
"""创建1孔子弹夹 - 中心单孔"""
# 计算1个洞位的坐标中心位置
center_x = size_x / 2
center_y = size_y / 2
locations = [
Coordinate(center_x, center_y, size_z - hole_depth), # 中心
]
return magazine_factory(
name=name,
size_x=size_x,
size_y=size_y,
size_z=size_z,
locations=locations,
hole_diameter=hole_diameter,
hole_depth=hole_depth,
max_sheets_per_hole=max_sheets_per_hole,
category="clip_magazine_one",
)
def MagazineHolder_6(
name: str,
size_x: float = 80.0,
size_y: float = 80.0,
size_z: float = 40.0,
hole_diameter: float = 14.0,
hole_depth: float = 10.0,
hole_spacing: float = 20.0,
max_sheets_per_hole: int = 100,
) -> MagazineHolder:
"""创建6孔子弹夹 - 六边形排布"""
# 计算6个洞位的坐标六边形排布中心1个周围5个
center_x = size_x / 2
center_y = size_y / 2
locations = []
# 周围6个孔按六边形排布
for i in range(6):
angle = i * 60 * math.pi / 180 # 每60度一个孔
x = center_x + hole_spacing * math.cos(angle)
y = center_y + hole_spacing * math.sin(angle)
locations.append(Coordinate(x, y, size_z - hole_depth))
return magazine_factory(
name=name,
size_x=size_x,
size_y=size_y,
size_z=size_z,
locations=locations,
hole_diameter=hole_diameter,
hole_depth=hole_depth,
max_sheets_per_hole=max_sheets_per_hole,
category="clip_magazine_six",
)