feat: finish legacy project and move it as packaged python project
This commit is contained in:
@@ -1,7 +1,20 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "lcr-connector"
|
name = "lcr-connector"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
description = "Use as much 3 devices to reach target value for resistor, capacitor and inductor."
|
description = "Use as much 3 devices to reach target value for resistor, capacitor and inductor."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
authors = [
|
||||||
|
{ name = "yyc12345", email = "yyc12321@outlook.com" }
|
||||||
|
]
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
|
classifiers = [
|
||||||
|
"Private :: Do Not Upload",
|
||||||
|
]
|
||||||
dependencies = []
|
dependencies = []
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
lcr-connector = "lcr_connector:main"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["uv_build>=0.8.0,<0.9"]
|
||||||
|
build-backend = "uv_build"
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ import enum
|
|||||||
import argparse
|
import argparse
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TypeVar, cast
|
from typing import TypeVar, Callable
|
||||||
from common import Circuit, CircuitDeviceScale, JointKind
|
from .common import Circuit, CircuitDeviceScale, JointKind, DeviceKind
|
||||||
from dataset import (
|
from .dataset import (
|
||||||
DatasetCollection,
|
DatasetCollection,
|
||||||
to_human_readable_value,
|
to_human_readable_value,
|
||||||
from_human_readable_value,
|
from_human_readable_value,
|
||||||
)
|
)
|
||||||
from query import Response
|
from .query import Request, ResponsePriority, Response
|
||||||
from resolver import Resolver, LutResolver, AStarResolver
|
from .resolver import Resolver, LutResolver, AStarResolver
|
||||||
|
|
||||||
_T = TypeVar("_T", bound=str)
|
_TStrEnum = TypeVar("_TStrEnum", bound=enum.StrEnum)
|
||||||
|
|
||||||
|
|
||||||
class AppResolver(enum.StrEnum):
|
class AppResolver(enum.StrEnum):
|
||||||
@@ -74,13 +74,46 @@ class App:
|
|||||||
"""
|
"""
|
||||||
print("LCR Connector")
|
print("LCR Connector")
|
||||||
print('Type "help" for more info. Type "exit" to quit.')
|
print('Type "help" for more info. Type "exit" to quit.')
|
||||||
print("query: do a query.")
|
self.__op_main()
|
||||||
print("help: show all command.")
|
|
||||||
print("exit: exit this app.")
|
|
||||||
|
|
||||||
|
class MainCmd(enum.StrEnum):
|
||||||
|
QUERY = "query"
|
||||||
|
HELP = "help"
|
||||||
|
EXIT = "exit"
|
||||||
|
|
||||||
|
class QueryDeviceChoice(enum.StrEnum):
|
||||||
|
RESISTOR = "r"
|
||||||
|
CAPACITOR = "c"
|
||||||
|
INDUCTOR = "l"
|
||||||
|
|
||||||
|
def to_device_kind(self) -> DeviceKind:
|
||||||
|
match self:
|
||||||
|
case App.QueryDeviceChoice.RESISTOR:
|
||||||
|
return DeviceKind.RESISTOR
|
||||||
|
case App.QueryDeviceChoice.CAPACITOR:
|
||||||
|
return DeviceKind.CAPACITOR
|
||||||
|
case App.QueryDeviceChoice.INDUCTOR:
|
||||||
|
return DeviceKind.INDUCTOR
|
||||||
|
|
||||||
|
class QuerySortPriority(enum.StrEnum):
|
||||||
|
LESS_DEVICES = "l"
|
||||||
|
MORE_ACCURACY = "a"
|
||||||
|
|
||||||
|
def to_response_priority(self) -> ResponsePriority:
|
||||||
|
match self:
|
||||||
|
case App.QuerySortPriority.LESS_DEVICES:
|
||||||
|
return ResponsePriority.LESS_DEVICES
|
||||||
|
case App.QuerySortPriority.MORE_ACCURACY:
|
||||||
|
return ResponsePriority.MORE_ACCURACY
|
||||||
|
|
||||||
|
class PageViewerCmd(enum.StrEnum):
|
||||||
|
PREVIOUS_PAGE = "f"
|
||||||
|
NEXT_PAGE = "b"
|
||||||
|
QUIT = "q"
|
||||||
|
|
||||||
|
def __op_main(self) -> None:
|
||||||
while True:
|
while True:
|
||||||
shit = self.__accept_command(("query", "help", "exit"))
|
match self.__accept_command(App.MainCmd):
|
||||||
match self.__accept_command(("query", "help", "exit")):
|
|
||||||
case "query":
|
case "query":
|
||||||
self.__op_query()
|
self.__op_query()
|
||||||
case "help":
|
case "help":
|
||||||
@@ -93,7 +126,35 @@ class App:
|
|||||||
break
|
break
|
||||||
|
|
||||||
def __op_query(self) -> None:
|
def __op_query(self) -> None:
|
||||||
pass
|
# collecting request infos
|
||||||
|
print("What are you connecting?")
|
||||||
|
print("r: resistor")
|
||||||
|
print("l: inductor")
|
||||||
|
print("c: capacitor")
|
||||||
|
device_kind = self.__accept_command(App.QueryDeviceChoice).to_device_kind()
|
||||||
|
|
||||||
|
print("Your target value?")
|
||||||
|
print('Example: "2.1k", "0.75m", "3.2M" and etc.')
|
||||||
|
target_value = self.__accept_device_value()
|
||||||
|
|
||||||
|
print("Your tolerance?")
|
||||||
|
print('It can be absolute value like "2.1k".')
|
||||||
|
print('Or relative value to your target value like "19.5%".')
|
||||||
|
tolerance = self.__accept_device_value_tolerance(target_value)
|
||||||
|
|
||||||
|
print("How to sort result?")
|
||||||
|
print("l: less component")
|
||||||
|
print("a: more accuracy")
|
||||||
|
response_priority = self.__accept_command(
|
||||||
|
App.QuerySortPriority
|
||||||
|
).to_response_priority()
|
||||||
|
|
||||||
|
# build request and ask resolver
|
||||||
|
request = Request(device_kind, target_value, tolerance, response_priority, 100)
|
||||||
|
response = self.__resolver.resolve(request)
|
||||||
|
|
||||||
|
# use page viewer to show result
|
||||||
|
self.__op_page_viewer(response)
|
||||||
|
|
||||||
def __op_page_viewer(self, response: Response) -> None:
|
def __op_page_viewer(self, response: Response) -> None:
|
||||||
cnt = len(response)
|
cnt = len(response)
|
||||||
@@ -127,37 +188,95 @@ class App:
|
|||||||
print("Page {} of {}.".format(current_page + 1, all_page + 1))
|
print("Page {} of {}.".format(current_page + 1, all_page + 1))
|
||||||
print("f: previous page. b: next page. q: quit this viewer.")
|
print("f: previous page. b: next page. q: quit this viewer.")
|
||||||
# check command
|
# check command
|
||||||
match self.__accept_command(("f", "b", "q")):
|
match self.__accept_command(App.PageViewerCmd):
|
||||||
case "f":
|
case App.PageViewerCmd.PREVIOUS_PAGE:
|
||||||
current_page = max(0, current_page - 1)
|
current_page = max(0, current_page - 1)
|
||||||
case "b":
|
case App.PageViewerCmd.NEXT_PAGE:
|
||||||
current_page = min(all_page, current_page + 1)
|
current_page = min(all_page, current_page + 1)
|
||||||
case "q":
|
case App.PageViewerCmd.QUIT:
|
||||||
break
|
break
|
||||||
|
|
||||||
def __accept_command(self, valid_commands: tuple[_T, ...]) -> _T:
|
def __accept_command(self, cmd_enum: type[_TStrEnum]) -> _TStrEnum:
|
||||||
|
"""
|
||||||
|
Accept a command from the user.
|
||||||
|
|
||||||
|
:param cmd_enum: The type of the command. It must be a subclass of `enum.StrEnum`.
|
||||||
|
:return: The command. It is an instance of `cmd_enum`.
|
||||||
|
"""
|
||||||
while True:
|
while True:
|
||||||
print("> ", end=None)
|
print("> ", end=None)
|
||||||
words = input()
|
words = input()
|
||||||
words = words.strip()
|
words = words.strip()
|
||||||
if words in valid_commands:
|
if words in cmd_enum:
|
||||||
return cast(_T, words)
|
return cmd_enum(words)
|
||||||
print("Unknown command, please try again.")
|
print("Unknown command, please try again.")
|
||||||
|
|
||||||
def __accept_value_input(self) -> float:
|
def __accept_device_value(self) -> float:
|
||||||
|
while True:
|
||||||
|
words = input()
|
||||||
|
value = self.__parse_human_readable_value(words)
|
||||||
|
if value is None:
|
||||||
|
print("Wrong value, please try again.")
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __accept_device_value_tolerance(self, target_value: float) -> float:
|
||||||
while True:
|
while True:
|
||||||
words = input()
|
words = input()
|
||||||
|
|
||||||
try:
|
if words.endswith("%"):
|
||||||
pending = from_human_readable_value(words)
|
value = self.__parse_plain_float(
|
||||||
except ValueError:
|
words[:-1], lambda x: x >= 0 and x <= 100
|
||||||
print("Wrong value, please try again.")
|
)
|
||||||
continue
|
if value is not None:
|
||||||
|
value = value / 100 * target_value
|
||||||
if pending > 0:
|
|
||||||
return pending
|
|
||||||
else:
|
else:
|
||||||
print("Value out of range, please try again.")
|
value = self.__parse_human_readable_value(words)
|
||||||
|
|
||||||
|
if value is None:
|
||||||
|
print("Wrong value, please try again.")
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __parse_plain_float(
|
||||||
|
self, user_value: str, checker: Callable[[float], bool]
|
||||||
|
) -> float | None:
|
||||||
|
"""
|
||||||
|
Parse a plain float value.
|
||||||
|
|
||||||
|
:param user_value: The value to parse.
|
||||||
|
:param checker: A function that checks if the input is valid.
|
||||||
|
It takes a float as input and returns a bool. True means the input is valid, otherwise False.
|
||||||
|
:return: The parsed value if it is valid, otherwise None.
|
||||||
|
"""
|
||||||
|
# try parsing it first
|
||||||
|
try:
|
||||||
|
value = float(user_value)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
# then check it by checker
|
||||||
|
if checker(value):
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __parse_human_readable_value(self, user_value: str) -> float | None:
|
||||||
|
"""
|
||||||
|
Parse a human-readable device value.
|
||||||
|
|
||||||
|
:param user_value: The value to parse.
|
||||||
|
:return: The parsed value if it is valid, otherwise None.
|
||||||
|
"""
|
||||||
|
# parse it
|
||||||
|
try:
|
||||||
|
value = from_human_readable_value(user_value)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
# then check its range
|
||||||
|
if value > 0:
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def __get_joint_kind_symbol(self, joint_kind: JointKind) -> str:
|
def __get_joint_kind_symbol(self, joint_kind: JointKind) -> str:
|
||||||
match joint_kind:
|
match joint_kind:
|
||||||
@@ -188,7 +307,7 @@ class App:
|
|||||||
print(f" └ {dev3}")
|
print(f" └ {dev3}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def main() -> None:
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
prog="LCR Connector",
|
prog="LCR Connector",
|
||||||
description="Get the resistor, capacitor, or inductor circuit which has the closest value for your given value within at most 3 devices.",
|
description="Get the resistor, capacitor, or inductor circuit which has the closest value for your given value within at most 3 devices.",
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from common import LcrConnException
|
from .common import LcrConnException
|
||||||
|
|
||||||
|
|
||||||
class Dataset:
|
class Dataset:
|
||||||
@@ -2,7 +2,7 @@ import enum
|
|||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
from common import DeviceKind, Circuit
|
from .common import DeviceKind, Circuit
|
||||||
|
|
||||||
|
|
||||||
class ResponsePriority(enum.Enum):
|
class ResponsePriority(enum.Enum):
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
from .common import Resolver, Request, ResultPriority
|
from .common import Resolver
|
||||||
from ..dataset import DatasetCollection
|
from ..dataset import DatasetCollection
|
||||||
from ..common import Circuit
|
from ..common import Circuit
|
||||||
|
from ..query import Request, Response
|
||||||
|
|
||||||
class AStarResolver(Resolver):
|
class AStarResolver(Resolver):
|
||||||
"""
|
"""
|
||||||
4
legacy/uv.lock
generated
4
legacy/uv.lock
generated
@@ -4,5 +4,5 @@ requires-python = ">=3.11"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lcr-connector"
|
name = "lcr-connector"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
source = { virtual = "." }
|
source = { editable = "." }
|
||||||
|
|||||||
Reference in New Issue
Block a user