- Every component now has a `url` (schema + JSON) pointing to the shop it was priced from; surfaced in the run detail and the BOM (new "Купить" column). Wire/switch/capacitor entries also expose gauge, ratings, single-vs-bank values; detail reports coil inner/outer diameter (=tube outer), tube inner bore + wall, projectile mass in grams and bore fit. - Animation was wrong: it stitched flight+discharge segments by sorting on time, which tore the trajectory into discontinuities. Rebuilt it sequentially -- coast (ballistic) between discharges + exact discharge trajectory, global x = coil center + local discharge_x -- so the slug moves continuously and coils sit at their real positions. Cleaner visuals: labeled coils that glow gold on discharge, blue slug. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
112 lines
3.5 KiB
Python
112 lines
3.5 KiB
Python
"""Типизированные описания реальных, продающихся в розницу компонентов."""
|
||
|
||
from dataclasses import dataclass
|
||
from typing import Literal
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class WireSpec:
|
||
part_id: str
|
||
material: Literal["copper", "aluminum"]
|
||
gauge_mm: float
|
||
insulation_od_mm: float
|
||
resistivity_ohm_m: float
|
||
max_current_a: float
|
||
price_per_m: float
|
||
source: str
|
||
url: str | None = None # прямая ссылка на магазин, где купить
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class CapacitorSpec:
|
||
part_number: str
|
||
capacitance_uf: float
|
||
voltage_v: float
|
||
esr_ohm: float
|
||
max_current_a: float
|
||
price: float
|
||
source: str
|
||
url: str | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class CapacitorBank:
|
||
"""Батарея из одинаковых конденсаторов: n_series (напряжение) × n_parallel (ток/ёмкость).
|
||
|
||
Последовательное соединение складывает напряжение и ESR, делит ёмкость;
|
||
параллельное — складывает ёмкость, максимальный ток и делит ESR. Итоговая
|
||
батарея выставляет те же поля, что и одиночный конденсатор
|
||
(`capacitance_uf`, `voltage_v`, `esr_ohm`, `max_current_a`, `price`),
|
||
поэтому используется как drop-in замена `CapacitorSpec` в StageConfig.
|
||
"""
|
||
|
||
base_part_number: str
|
||
n_series: int
|
||
n_parallel: int
|
||
capacitance_uf: float
|
||
voltage_v: float
|
||
esr_ohm: float
|
||
max_current_a: float
|
||
price: float
|
||
url: str | None = None
|
||
|
||
@property
|
||
def count(self) -> int:
|
||
return self.n_series * self.n_parallel
|
||
|
||
@classmethod
|
||
def from_spec(cls, spec: "CapacitorSpec", n_series: int, n_parallel: int) -> "CapacitorBank":
|
||
n_series = max(1, int(n_series))
|
||
n_parallel = max(1, int(n_parallel))
|
||
return cls(
|
||
base_part_number=spec.part_number,
|
||
n_series=n_series,
|
||
n_parallel=n_parallel,
|
||
capacitance_uf=spec.capacitance_uf * n_parallel / n_series,
|
||
voltage_v=spec.voltage_v * n_series,
|
||
esr_ohm=spec.esr_ohm * n_series / n_parallel,
|
||
max_current_a=spec.max_current_a * n_parallel,
|
||
price=spec.price * n_series * n_parallel,
|
||
url=spec.url,
|
||
)
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class SwitchSpec:
|
||
part_number: str
|
||
kind: Literal["SCR", "MOSFET", "IGBT"]
|
||
max_current_a: float
|
||
max_voltage_v: float
|
||
on_resistance_ohm: float | None
|
||
on_voltage_drop_v: float | None
|
||
turn_on_time_ns: float
|
||
price: float
|
||
source: str
|
||
url: str | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class SensorSpec:
|
||
part_number: str
|
||
kind: Literal["inductive", "optical", "hall"]
|
||
propagation_delay_ns: float
|
||
price: float
|
||
source: str
|
||
sensitivity_v_per_mps: float | None = None
|
||
threshold_v: float | None = None
|
||
url: str | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class ProjectileMaterialSpec:
|
||
name: str
|
||
density_kg_m3: float
|
||
mu_r: float
|
||
b_sat_tesla: float
|
||
price_per_kg: float
|
||
source: str
|
||
# электрическое удельное сопротивление — для расчёта вихревых потерь в снаряде
|
||
# (сплошной проводник в импульсном поле). Дефолт ~ конструкционная сталь.
|
||
resistivity_ohm_m: float = 1.6e-7
|
||
url: str | None = None
|