refactor: refactoring old code
This commit is contained in:
15
legacy/.gitignore
vendored
Normal file
15
legacy/.gitignore
vendored
Normal 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
1
legacy/.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.11
|
||||
3
legacy/README.md
Normal file
3
legacy/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# LCR Connector (Legacy)
|
||||
|
||||
在3个元器件内,使用给定元器件数值列表快速找到目标数值元器件的最好拼接方式,支持电阻,电容,电感
|
||||
174
legacy/common.py
Normal file
174
legacy/common.py
Normal 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
61
legacy/dataset.py
Normal 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
36
legacy/lcr_connector.py
Normal 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")
|
||||
345
legacy/main.py
Normal file
345
legacy/main.py
Normal file
@@ -0,0 +1,345 @@
|
||||
import math
|
||||
import struct
|
||||
import os
|
||||
import sys
|
||||
|
||||
OneComponentList = []
|
||||
TwoComponentList = []
|
||||
ThreeComponentList = []
|
||||
|
||||
ResultList = []
|
||||
|
||||
class OneComponent(object):
|
||||
Value1 = 0.0
|
||||
|
||||
Value = 0.0
|
||||
def PrintCircuit(self):
|
||||
print(OutputAsHuman(self.Value1))
|
||||
|
||||
class TwoComponent(object):
|
||||
Value1 = 0.0
|
||||
Value2 = 0.0
|
||||
IsSeries = True
|
||||
|
||||
Value = 0.0
|
||||
def PrintCircuit(self):
|
||||
print("[{}] ┬ {}".format('S' if self.IsSeries else 'P', OutputAsHuman(self.Value1)))
|
||||
print(" └ {}".format(OutputAsHuman(self.Value2)))
|
||||
|
||||
class ThreeComponent(object):
|
||||
Value1 = 0.0
|
||||
Value2 = 0.0
|
||||
Value3 = 0.0
|
||||
IsSeries1 = True
|
||||
IsSeries2 = True
|
||||
|
||||
Value = 0.0
|
||||
def PrintCircuit(self):
|
||||
print("[{}] ┬ [{}] ┬ {}".format('S' if self.IsSeries2 else 'P', 'S' if self.IsSeries1 else 'P', OutputAsHuman(self.Value1)))
|
||||
print(" │ └ {}".format(OutputAsHuman(self.Value2)))
|
||||
print(" └ {}".format(OutputAsHuman(self.Value3)))
|
||||
|
||||
class ResultStruct(object):
|
||||
Subtraction = 0.0
|
||||
ComponentCount = 0
|
||||
CorrespondingClass = None
|
||||
|
||||
def LoadFromFile(name):
|
||||
f = open(name, 'r', encoding = 'utf-8')
|
||||
|
||||
# read file to get one elements state
|
||||
while True:
|
||||
cache = f.readline()
|
||||
if cache == '':
|
||||
break
|
||||
|
||||
(status, value) = InputAsHuman(cache.strip())
|
||||
if status:
|
||||
newobj = None
|
||||
newobj = OneComponent()
|
||||
newobj.Value = value
|
||||
newobj.Value1 = value
|
||||
OneComponentList.append(newobj)
|
||||
|
||||
# construct two component list
|
||||
length = len(OneComponentList)
|
||||
for i_index in range(length):
|
||||
for j_index in range(i_index, length):
|
||||
i = OneComponentList[i_index]
|
||||
j = OneComponentList[j_index]
|
||||
|
||||
# series
|
||||
newobj = TwoComponent()
|
||||
newobj.Value1 = i.Value
|
||||
newobj.Value2 = j.Value
|
||||
newobj.IsSeries = True
|
||||
newobj.Value = i.Value + j.Value
|
||||
TwoComponentList.append(newobj)
|
||||
|
||||
# parallel
|
||||
newobj = TwoComponent()
|
||||
newobj.Value1 = i.Value
|
||||
newobj.Value2 = j.Value
|
||||
newobj.IsSeries = False
|
||||
newobj.Value = (i.Value * j.Value) / (i.Value + j.Value)
|
||||
TwoComponentList.append(newobj)
|
||||
|
||||
# construct three component list
|
||||
for i in OneComponentList:
|
||||
for j in TwoComponentList:
|
||||
# series
|
||||
newobj = ThreeComponent()
|
||||
newobj.Value1 = j.Value1
|
||||
newobj.Value2 = j.Value2
|
||||
newobj.Value3 = i.Value
|
||||
newobj.IsSeries1 = j.IsSeries
|
||||
newobj.IsSeries2 = True
|
||||
newobj.Value = j.Value + i.Value
|
||||
ThreeComponentList.append(newobj)
|
||||
|
||||
# parallel
|
||||
newobj = ThreeComponent()
|
||||
newobj.Value1 = j.Value1
|
||||
newobj.Value2 = j.Value2
|
||||
newobj.Value3 = i.Value
|
||||
newobj.IsSeries1 = j.IsSeries
|
||||
newobj.IsSeries2 = False
|
||||
newobj.Value = (j.Value * i.Value) / (j.Value + i.Value)
|
||||
ThreeComponentList.append(newobj)
|
||||
|
||||
f.close()
|
||||
SaveAsCache(name)
|
||||
|
||||
def LoadFromCache(name):
|
||||
f = open(name + '.cache', 'rb')
|
||||
counter = 0
|
||||
(counter, ) = struct.unpack("I", f.read(4))
|
||||
for i in range(counter):
|
||||
newobj = OneComponent()
|
||||
newobj.Value1 = ReadDouble(f)
|
||||
newobj.Value = ReadDouble(f)
|
||||
OneComponentList.append(newobj)
|
||||
(counter, ) = struct.unpack("I", f.read(4))
|
||||
for i in range(counter):
|
||||
newobj = TwoComponent()
|
||||
newobj.Value1 = ReadDouble(f)
|
||||
newobj.Value2 = ReadDouble(f)
|
||||
newobj.IsSeries = ReadBoolean(f)
|
||||
newobj.Value = ReadDouble(f)
|
||||
TwoComponentList.append(newobj)
|
||||
(counter, ) = struct.unpack("I", f.read(4))
|
||||
for i in range(counter):
|
||||
newobj = ThreeComponent()
|
||||
newobj.Value1 = ReadDouble(f)
|
||||
newobj.Value2 = ReadDouble(f)
|
||||
newobj.Value3 = ReadDouble(f)
|
||||
newobj.IsSeries1 = ReadBoolean(f)
|
||||
newobj.IsSeries2 = ReadBoolean(f)
|
||||
newobj.Value = ReadDouble(f)
|
||||
ThreeComponentList.append(newobj)
|
||||
|
||||
f.close()
|
||||
|
||||
def SaveAsCache(name):
|
||||
# in cache, is series should follow resistor mode
|
||||
f = open(name + '.cache', 'wb')
|
||||
WriteInt(f, len(OneComponentList))
|
||||
for i in OneComponentList:
|
||||
WriteDouble(f, i.Value1)
|
||||
WriteDouble(f, i.Value)
|
||||
WriteInt(f, len(TwoComponentList))
|
||||
for i in TwoComponentList:
|
||||
WriteDouble(f, i.Value1)
|
||||
WriteDouble(f, i.Value2)
|
||||
WriteBoolean(f, i.IsSeries)
|
||||
WriteDouble(f, i.Value)
|
||||
WriteInt(f, len(ThreeComponentList))
|
||||
for i in ThreeComponentList:
|
||||
WriteDouble(f, i.Value1)
|
||||
WriteDouble(f, i.Value2)
|
||||
WriteDouble(f, i.Value3)
|
||||
WriteBoolean(f, i.IsSeries1)
|
||||
WriteBoolean(f, i.IsSeries2)
|
||||
WriteDouble(f, i.Value)
|
||||
|
||||
f.close()
|
||||
|
||||
def ReadDouble(fs):
|
||||
return struct.unpack("d", fs.read(8))[0]
|
||||
|
||||
def ReadInt(fs):
|
||||
return struct.unpack("I", fs.read(4))[0]
|
||||
|
||||
def ReadBoolean(fs):
|
||||
return struct.unpack("?", fs.read(1))[0]
|
||||
|
||||
def WriteDouble(fs, num):
|
||||
fs.write(struct.pack("d", num))
|
||||
|
||||
def WriteInt(fs, num):
|
||||
fs.write(struct.pack("I", num))
|
||||
|
||||
def WriteBoolean(fs, num):
|
||||
fs.write(struct.pack("?", num))
|
||||
|
||||
def OutputAsHuman(v):
|
||||
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)
|
||||
|
||||
return "{:e}".format(v)
|
||||
|
||||
def InputAsHuman(strl):
|
||||
try:
|
||||
if strl.endswith('n'):
|
||||
return (True, float(strl[0:-1]) * 1e-12)
|
||||
if strl.endswith('p'):
|
||||
return (True, float(strl[0:-1]) * 1e-9)
|
||||
if strl.endswith('u'):
|
||||
return (True, float(strl[0:-1]) * 1e-6)
|
||||
if strl.endswith('m'):
|
||||
return (True, float(strl[0:-1]) * 1e-3)
|
||||
if strl.endswith('k'):
|
||||
return (True, float(strl[0:-1]) * 1e3)
|
||||
if strl.endswith('M'):
|
||||
return (True, float(strl[0:-1]) * 1e6)
|
||||
return (True, float(strl))
|
||||
except:
|
||||
return (False, 0.0)
|
||||
|
||||
def ValidCommandInput(valid_list):
|
||||
while True:
|
||||
cache = input()
|
||||
if cache not in valid_list:
|
||||
print('Wrong command, please input again.')
|
||||
else:
|
||||
return cache
|
||||
|
||||
def ValidNumberInput():
|
||||
while True:
|
||||
cache = input()
|
||||
(status, num) = InputAsHuman(cache)
|
||||
if not status:
|
||||
print('Wrong number, please input again.')
|
||||
else:
|
||||
return num
|
||||
|
||||
def DoQuery():
|
||||
# get config
|
||||
|
||||
print('What are you connecting?')
|
||||
print('r: resistor')
|
||||
print('l: inductor')
|
||||
print('c: capacitor')
|
||||
mode = ValidCommandInput(['c', 'l', 'r'])
|
||||
is_resistor_mode = mode != 'c'
|
||||
|
||||
print('Your target value?')
|
||||
target = ValidNumberInput()
|
||||
|
||||
print('Your tolerance?')
|
||||
target_tolerance = ValidNumberInput()
|
||||
|
||||
print('How to sort result?')
|
||||
print('l: less component')
|
||||
print('a: more accuracy')
|
||||
sort_mode = ValidCommandInput(['l', 'a'])
|
||||
|
||||
# start computing
|
||||
print('Collecting and sorting data...')
|
||||
ResultList.clear()
|
||||
for i in OneComponentList:
|
||||
cache = i.Value - target
|
||||
if abs(cache) < target_tolerance:
|
||||
newobj = ResultStruct()
|
||||
newobj.Subtraction = cache
|
||||
newobj.ComponentCount = 1
|
||||
newobj.CorrespondingClass = i
|
||||
ResultList.append(newobj)
|
||||
for i in TwoComponentList:
|
||||
cache = i.Value - target
|
||||
if abs(cache) < target_tolerance:
|
||||
newobj = ResultStruct()
|
||||
newobj.Subtraction = cache
|
||||
newobj.ComponentCount = 2
|
||||
newobj.CorrespondingClass = i
|
||||
ResultList.append(newobj)
|
||||
for i in ThreeComponentList:
|
||||
cache = i.Value - target
|
||||
if abs(cache) < target_tolerance:
|
||||
newobj = ResultStruct()
|
||||
newobj.Subtraction = cache
|
||||
newobj.ComponentCount = 3
|
||||
newobj.CorrespondingClass = i
|
||||
ResultList.append(newobj)
|
||||
|
||||
if sort_mode == 'l':
|
||||
ResultList.sort(key = lambda x: (x.ComponentCount, abs(x.Subtraction)))
|
||||
else:
|
||||
ResultList.sort(key = lambda x: abs(x.Subtraction))
|
||||
|
||||
# display result
|
||||
if len(ResultList) == 0:
|
||||
print('Sorry, no result!')
|
||||
return
|
||||
|
||||
count = len(ResultList)
|
||||
all_page = int(count / 10)
|
||||
current_page = 0
|
||||
while current_page <= all_page:
|
||||
for i in range(9):
|
||||
picked_index = current_page * 9 + i
|
||||
if (picked_index < count):
|
||||
picked_item = ResultList[picked_index]
|
||||
print("Plan {}\t{}\t{:.2%}".format(picked_index + 1, OutputAsHuman(picked_item.CorrespondingClass.Value), picked_item.Subtraction / target))
|
||||
picked_item.CorrespondingClass.PrintCircuit()
|
||||
|
||||
print('')
|
||||
print("Page {} of {}. p: next page. q: exit".format(current_page + 1, all_page + 1))
|
||||
command = ValidCommandInput(['p', 'q'])
|
||||
if command == 'p':
|
||||
current_page += 1
|
||||
elif command == 'q':
|
||||
break
|
||||
|
||||
def DoHelp():
|
||||
print('LCR Connect Help:')
|
||||
print('')
|
||||
print('query: do a query immediately. following the guider and find the result.')
|
||||
print('help: print this')
|
||||
print('exit: exit this app')
|
||||
|
||||
# ===================================================== program start
|
||||
|
||||
print('LCR Connect')
|
||||
|
||||
# loading file
|
||||
print('Input the component value list file name:')
|
||||
filename = input()
|
||||
if not os.path.isfile(filename + '.cache'):
|
||||
LoadFromFile(filename)
|
||||
else:
|
||||
LoadFromCache(filename)
|
||||
|
||||
# ready for command
|
||||
while True:
|
||||
sys.stdout.write('> ')
|
||||
sys.stdout.flush()
|
||||
cmd = ValidCommandInput(['exit', 'query', 'help'])
|
||||
if cmd == 'exit':
|
||||
break
|
||||
elif cmd == 'query':
|
||||
DoQuery()
|
||||
elif cmd == 'help':
|
||||
DoHelp()
|
||||
7
legacy/pyproject.toml
Normal file
7
legacy/pyproject.toml
Normal 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 = []
|
||||
0
legacy/resolver/__init__.py
Normal file
0
legacy/resolver/__init__.py
Normal file
15
legacy/resolver/astar.py
Normal file
15
legacy/resolver/astar.py
Normal 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
102
legacy/resolver/common.py
Normal 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
109
legacy/resolver/lut.py
Normal 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
8
legacy/uv.lock
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
version = 1
|
||||
revision = 2
|
||||
requires-python = ">=3.11"
|
||||
|
||||
[[package]]
|
||||
name = "lcr-connector"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
Reference in New Issue
Block a user