1
0

refactor: refactoring old code

This commit is contained in:
2026-06-02 21:08:48 +08:00
parent 0a165dc906
commit 0812e065b5
15 changed files with 534 additions and 1 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
## ======== Personal ========
.vscode/

View File

@@ -1,3 +1,3 @@
# LCR Connector
在3个元器件内使用给定元器件数值列表快速找到目标数值元器件的最好拼接方式支持电阻电容电感
TODO

15
legacy/.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
## ======== Personal ========
## ======== Python ========
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info
# Virtual environments
.venv

1
legacy/.python-version Normal file
View File

@@ -0,0 +1 @@
3.11

3
legacy/README.md Normal file
View File

@@ -0,0 +1,3 @@
# LCR Connector (Legacy)
在3个元器件内使用给定元器件数值列表快速找到目标数值元器件的最好拼接方式支持电阻电容电感

174
legacy/common.py Normal file
View File

@@ -0,0 +1,174 @@
import enum
from dataclasses import dataclass
class LcrConnException(Exception):
"""The exception thrown by LCR Connector"""
pass
class DeviceKind(enum.IntEnum):
"""The kind of device"""
RESISTOR = enum.auto()
"""Resistor device"""
CAPACITOR = enum.auto()
"""Capacitor device"""
INDUCTOR = enum.auto()
"""Inductor device"""
class JointKind(enum.IntEnum):
"""The joint type between 2 devices"""
SERIES = enum.auto()
"""Series connection"""
PARALLEL = enum.auto()
"""Parallel connection"""
def flip(self) -> 'JointKind':
"""
Flip the joint kind
Flip the joint kind from series to parallel or vice versa
:return: The flipped joint kind
"""
match self:
case JointKind.SERIES:
return JointKind.PARALLEL
case JointKind.PARALLEL:
return JointKind.SERIES
@dataclass
class CircuitJoint:
"""The part of circuit composed of two devices and the joint kind"""
device_value: float
"""The value of the device"""
joint_kind: JointKind
"""The joint kind between this device and the next device"""
def compute(self, value: float, device_kind: DeviceKind) -> float:
"""
Compute the joint value
:param value: The value computed from previous devices
:param device_kind: The kind of the device
:return: The joint value computed
"""
if self.device_value <= 0 or value <= 0:
raise LcrConnException("Device value must be greater than 0")
# We perform series connect for: series resistor, series inductor and parallel capacitor.
# We perform parallel connect for: parallel resistor, parallel inductor and series capacitor.
joint_kind = self.joint_kind
if device_kind == DeviceKind.CAPACITOR:
joint_kind = joint_kind.flip()
match joint_kind:
case JointKind.SERIES:
return self.device_value + value
case JointKind.PARALLEL:
return (self.device_value * value) / (self.device_value + value)
class Circuit:
"""The circuit composed of multiple joints"""
device_value: float
"""The value of the first device"""
joints: list[CircuitJoint]
"""The joints between devices"""
def __init__(self, value: float):
self.device_value = value
self.joints = []
def add_joint(self, joint: CircuitJoint):
self.joints.append(joint)
def len_devices(self) -> int:
return len(self.joints) + 1
def compute(self, device_kind: DeviceKind) -> float:
"""
Compute the circuit value
:param device_kind: The kind of the device
:return: The circuit value
"""
if self.device_value <= 0:
raise LcrConnException("Device value must be greater than 0")
value = self.device_value
for joint in self.joints:
value = joint.compute(value, device_kind)
return value
def from_human_readable_value(strl: str) -> float:
"""
Convert human readable value to float
:param strl: The human readable value
:return: The parsed float value
:raises ValueError: If the input string is not a valid number
"""
strl = strl.strip()
if strl.endswith('n'):
return float(strl[0:-1]) * 1e-12
if strl.endswith('p'):
return float(strl[0:-1]) * 1e-9
if strl.endswith('u'):
return float(strl[0:-1]) * 1e-6
if strl.endswith('m'):
return float(strl[0:-1]) * 1e-3
if strl.endswith('k'):
return float(strl[0:-1]) * 1e3
if strl.endswith('M'):
return float(strl[0:-1]) * 1e6
if strl.endswith('G'):
return float(strl[0:-1]) * 1e9
return float(strl)
def to_human_readable_value(v: float) -> str:
"""
Convert float value to human readable value
:param value: The float value
:return: The human readable value
"""
if v / 1e-12 < 1e3:
return "{:e} n".format(v / 1e-12)
if v / 1e-9 < 1e3:
return "{:.4f} p".format(v / 1e-9)
if v / 1e-6 < 1e3:
return "{:.4f} u".format(v / 1e-6)
if v / 1e-3 < 1e3:
return "{:.4f} m".format(v / 1e-3)
if v < 1e3:
return "{:.4f}".format(v)
if v / 1e3 < 1e3:
return "{:.4f} k".format(v / 1e3)
if v / 1e6 < 1e3:
return "{:.4f} M".format(v / 1e6)
if v / 1e9 < 1e3:
return "{:.4f} G".format(v / 1e9)
return "{:e}".format(v)

61
legacy/dataset.py Normal file
View File

@@ -0,0 +1,61 @@
from typing import Iterable
from pathlib import Path
from dataclasses import dataclass
from .common import LcrConnException, from_human_readable_value
@dataclass(frozen=True)
class DataSubset:
"""A list holding available device gauge values for resistor, capacitor or inductor"""
gauges: tuple[float, ...]
"""A list of available device gauge values"""
@staticmethod
def from_iterable(str_gauges: Iterable[str]) -> "DataSubset":
# Remove redundant parts
gauges_set: set[float] = set()
for str_gauge in str_gauges:
numeric_gauge = from_human_readable_value(str_gauge)
if numeric_gauge in gauges_set:
raise LcrConnException(f"Duplicate gauge value found: {str_gauge}")
else:
gauges_set.add(numeric_gauge)
# Return value
return DataSubset(tuple(gauges_set))
@staticmethod
def from_file(filename: Path) -> "DataSubset":
with open(filename, "r", encoding="utf-8") as f:
legal_lines = filter(lambda line: line != "", (line.strip() for line in f))
return DataSubset.from_iterable(legal_lines)
@dataclass(frozen=True)
class DataSet:
"""The dataset include 3 lists of available device gauge values for resistor, capacitor and inductor"""
resistor: DataSubset
"""A list of available device gauge values for resistor"""
capacitor: DataSubset
"""A list of available device gauge values for capacitor"""
inductor: DataSubset
"""A list of available device gauge values for inductor"""
@staticmethod
def from_iterable(
resistor: Iterable[str], capacitor: Iterable[str], inductor: Iterable[str]
) -> "DataSet":
return DataSet(
DataSubset.from_iterable(resistor),
DataSubset.from_iterable(capacitor),
DataSubset.from_iterable(inductor),
)
@staticmethod
def from_file(resistor: Path, capacitor: Path, inductor: Path) -> "DataSet":
return DataSet(
DataSubset.from_file(resistor),
DataSubset.from_file(capacitor),
DataSubset.from_file(inductor),
)

36
legacy/lcr_connector.py Normal file
View File

@@ -0,0 +1,36 @@
from .common import LcrConnException, Circuit, JointKind, to_human_readable_value
def _get_joint_kind_symbol(joint_kind: JointKind) -> str:
match joint_kind:
case JointKind.SERIES:
return "S"
case JointKind.PARALLEL:
return "P"
def _illustrate_circuit(circuit: Circuit) -> None:
match circuit.len_devices():
case 1:
dev1 = to_human_readable_value(circuit.device_value)
print(f"{dev1}")
case 2:
dev1 = to_human_readable_value(circuit.device_value)
joint1 = circuit.joints[0]
j1 = _get_joint_kind_symbol(joint1.joint_kind)
dev2 = to_human_readable_value(joint1.device_value)
print(f"[{j1}] ┬ {dev1}")
print(f"{dev2}")
case 3:
dev1 = to_human_readable_value(circuit.device_value)
joint1 = circuit.joints[0]
j1 = _get_joint_kind_symbol(joint1.joint_kind)
dev2 = to_human_readable_value(joint1.device_value)
joint2 = circuit.joints[1]
j2 = _get_joint_kind_symbol(joint2.joint_kind)
dev3 = to_human_readable_value(joint2.device_value)
print(f"[{j2}] ┬ [{j1}] ┬ {dev1}")
print(f" │ └ {dev2}")
print(f"{dev3}")
case _:
raise LcrConnException("Circuit too complex to illustrate")

7
legacy/pyproject.toml Normal file
View File

@@ -0,0 +1,7 @@
[project]
name = "lcr-connector"
version = "0.1.0"
description = "Use as much 3 devices to reach target value for resistor, capacitor and inductor."
readme = "README.md"
requires-python = ">=3.11"
dependencies = []

View File

15
legacy/resolver/astar.py Normal file
View File

@@ -0,0 +1,15 @@
from typing import Iterator
from .common import Resolver, ResolverRequest, ResolverResult, ResultPriority
from ..dataset import DataSet
class AStarResolver(Resolver):
"""
A resolver that uses A* algorithm to find the best matching circuit.
"""
def __init__(self, dataset: DataSet):
pass
def resolve(self, request: ResolverRequest) -> Iterator[ResolverResult]:
pass

102
legacy/resolver/common.py Normal file
View File

@@ -0,0 +1,102 @@
import enum
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Iterator
from ..common import DeviceKind, Circuit
class ResultPriority(enum.Enum):
"""
The priority of the result.
"""
LESS_DEVICES = enum.auto()
"""Less devices is the first priority."""
MORE_ACCURACY = enum.auto()
"""More accuracy is the first priority."""
@dataclass
class ResolverRequest:
"""
The request object for the resolver.
"""
device_kind: DeviceKind
"""The kind of device to resolve."""
target_value: float
"""The target value of the device."""
tolerance: float
"""The tolerance of the device."""
result_priority: ResultPriority
"""The priority of the result."""
count_limit: int
"""The limit of the count of results."""
class ResolverResult:
"""
The result of the resolver.
"""
circuit: Circuit
"""The circuit of the result."""
__value_cache: float | None
"""The cache of the circuit value."""
__difference_cache: float | None
"""The cache of the difference between the target value and the circuit value."""
__relative_difference_cache: float | None
"""The cache of the relative difference between the target value and the circuit value."""
def __init__(self, circuit: Circuit):
self.circuit = circuit
self.__value_cache = None
self.__difference_cache = None
self.__relative_difference_cache = None
def compute(self, device_kind: DeviceKind) -> float:
"""
Compute the circuit value.
"""
if self.__value_cache is None:
self.__value_cache = self.circuit.compute(device_kind)
return self.__value_cache
def difference(self, target_value: float, device_kind: DeviceKind) -> float:
"""
Get the difference between the target value and the circuit value.
"""
if self.__difference_cache is None:
self.__difference_cache = abs(
target_value - self.circuit.compute(device_kind)
)
return self.__difference_cache
def relative_difference(
self, target_value: float, device_kind: DeviceKind
) -> float:
"""
Get the relative difference between the target value and the circuit value.
"""
if self.__relative_difference_cache is None:
self.__relative_difference_cache = (
abs(target_value - self.circuit.compute(device_kind)) / target_value
)
return self.__relative_difference_cache
def len_devices(self) -> int:
"""
Get the number of devices in the circuit.
"""
return self.circuit.len_devices()
class Resolver(ABC):
"""
Abstract base class for all resolvers.
"""
@abstractmethod
def resolve(self, request: ResolverRequest) -> Iterator[ResolverResult]:
pass

109
legacy/resolver/lut.py Normal file
View File

@@ -0,0 +1,109 @@
import struct
from typing import Iterator, BinaryIO
from pathlib import Path
from .common import Resolver, ResolverRequest, ResolverResult, ResultPriority
from ..dataset import DataSet
from ..common import Circuit, CircuitJoint, JointKind, LcrConnException
class LutResolver(Resolver):
"""
A resolver that uses a lookup table to find the best matching circuit.
"""
lut: tuple[Circuit]
def __init__(self, lut: tuple[Circuit]):
self.lut = lut
@staticmethod
def from_dataset(dataset: DataSet) -> 'LutResolver':
pass
@staticmethod
def from_cache(filename: Path) -> 'LutResolver':
with open(filename, "rb") as f:
cnt = _read_int(f)
return LutResolver(tuple(LutItem.from_cache(f) for _ in range(cnt)))
def save_as_cache(self, filename: Path) -> None:
with open(filename, "wb") as f:
_write_int(f, len(self.lut))
for item in self.lut:
item.save_as_cache(f)
def resolve(self, request: ResolverRequest) -> Iterator[ResolverResult]:
pass
class LutItem:
"""
An item in the lookup table.
"""
circuit: Circuit
"""The circuit represented by this item."""
__value_cache: float | None
"""The cached computed value of the circuit, or None if it has not been cached yet."""
def __init__(self, circuit: Circuit):
self.circuit = circuit
@staticmethod
def from_cache(f: BinaryIO) -> 'LutItem':
cnt = _read_int(f)
if cnt < 1:
raise LcrConnException("Invalid circuit count in LUT item")
device_value = _read_double(f)
circuit = Circuit(device_value)
cnt -= 1
for _ in range(cnt):
j = JointKind.SERIES if _read_bool(f) else JointKind.PARALLEL
dev = _read_double(f)
joint = CircuitJoint(j, dev)
circuit.add_joint(joint)
return LutItem(circuit)
def save_as_cache(self, f: BinaryIO) -> None:
_write_int(f, self.circuit.len_devices())
_write_double(f, self.circuit.device_value)
for joint in self.circuit.joints():
_write_bool(f, joint.kind == JointKind.SERIES)
_write_double(f, joint.value)
def compute(self) -> float:
"""The computed value of the circuit."""
if self.__value_cache is None:
self.__value_cache = self.circuit.value()
return self.__value_cache
DOUBLE_PACKER = struct.Struct("d")
INT_PACKER = struct.Struct("I")
BOOL_PACKER = struct.Struct("?")
def _read_double(fs) -> float:
return DOUBLE_PACKER.unpack(fs.read(DOUBLE_PACKER.size))[0]
def _read_int(fs) -> int:
return INT_PACKER.unpack(fs.read(INT_PACKER.size))[0]
def _read_bool(fs) -> bool:
return BOOL_PACKER.unpack(fs.read(BOOL_PACKER.size))[0]
def _write_double(fs, num: float):
fs.write(DOUBLE_PACKER.pack(num))
def _write_int(fs, num: int):
fs.write(INT_PACKER.pack(num))
def _write_bool(fs, num: bool):
fs.write(BOOL_PACKER.pack(num))

8
legacy/uv.lock generated Normal file
View File

@@ -0,0 +1,8 @@
version = 1
revision = 2
requires-python = ">=3.11"
[[package]]
name = "lcr-connector"
version = "0.1.0"
source = { virtual = "." }