From 5ad3e118365c66d9147909f005bee9c157d8e46e Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Sun, 28 Jun 2026 11:19:25 +0800 Subject: [PATCH] feat: improve response plan display --- legacy/src/lcr_connector/__init__.py | 153 +++++++++++++++++---------- legacy/src/lcr_connector/dataset.py | 5 +- legacy/src/lcr_connector/query.py | 31 +++++- 3 files changed, 127 insertions(+), 62 deletions(-) diff --git a/legacy/src/lcr_connector/__init__.py b/legacy/src/lcr_connector/__init__.py index 7bde024..98fa7d2 100644 --- a/legacy/src/lcr_connector/__init__.py +++ b/legacy/src/lcr_connector/__init__.py @@ -9,7 +9,7 @@ from .dataset import ( to_human_readable_value, from_human_readable_value, ) -from .query import Request, ResponsePriority, Response, ResponseItem +from .query import Request, ResponsePriority, Response, MAX_RESPONSE_CNT from .resolver import Resolver, LutResolver, BfsResolver _TStrEnum = TypeVar("_TStrEnum", bound=enum.StrEnum) @@ -76,6 +76,8 @@ class App: print('Type "help" for more info. Type "exit" to quit.') self.__op_main() + # region: Subcommand Processors + class MainCmd(enum.StrEnum): QUERY = "query" HELP = "help" @@ -179,17 +181,8 @@ class App: index = current_page * (ITEMS_PER_PAGE - 1) + i if index >= cnt: continue - # fetch item and print it - item = response[index] - print( - "Plan {0}\tValue: {1}\tDiff: {2} ({3:.2%})".format( - index + 1, - to_human_readable_value(item.value), - to_human_readable_value(item.difference), - item.relative_difference, - ) - ) - self.__illustrate_circuit(item.circuit, response.device_kind) + # and print it + self.__illustrate_response(response, index) # print page footer print("") @@ -204,6 +197,10 @@ class App: case App.PageViewerCmd.QUIT: break + # endregion + + # region: Command Utilities + def __accept_command(self, cmd_enum: type[_TStrEnum]) -> _TStrEnum: """ Accept a command from the user. @@ -223,8 +220,6 @@ class App: print("Unknown command, please try again.") def __accept_count_value(self) -> int: - MAX_COUNT: int = 50 - while True: self.__show_prompt_arrow() words = input() @@ -237,7 +232,7 @@ class App: print("Wrong value, please try again.") continue - if value > MAX_COUNT or value <= 0: + if value > MAX_RESPONSE_CNT or value <= 0: print("Wrong value, please try again.") else: return value @@ -319,6 +314,78 @@ class App: else: return None + # endregion + + # region: Response Display + + def __get_device_unit(self, device_kind: DeviceKind) -> str: + match device_kind: + case DeviceKind.RESISTOR: + return "\u2126" + case DeviceKind.CAPACITOR: + return "F" + case DeviceKind.INDUCTOR: + return "H" + + def __to_circult_graph_value(self, value: float, device_kind: DeviceKind) -> str: + # Remove sign and append device unit + return to_human_readable_value(value)[1:] + self.__get_device_unit(device_kind) + + def __to_plan_head_value(self, value: float, device_kind: DeviceKind) -> str: + # Remove sign and append device unit + return to_human_readable_value(value)[1:] + self.__get_device_unit(device_kind) + + def __to_plan_head_diff(self, value: float, device_kind: DeviceKind) -> str: + # Keep the sign and append device unit + return to_human_readable_value(value) + self.__get_device_unit(device_kind) + + def __to_plan_head_diff_pct(self, value: float) -> str: + # Keep the sign and format it as percentage style without trailing device unit + return "{:.2%}".format(value) + + # YYC MARK: + # The function showing circuit graph should be maintained carefully. + # First, we want they are show in console properly, + # And we also want they have good code view. + # + # I notices that the number part of the output of `to_human_readable_value` will only be + # "+999.9999" or "+9.9999e+00". So its maximum of its length is 11, considering the possibility, + # that the absolute value of exponential part is larger than 99, is close to zero. + # After putting the scale unit and device unit together like " nF", + # the whole maximum size of the built string is 14. + # + # So we need pick a larger number and odd number for the space for showing device value, + # because odd value can be divided by two so it can be split as two parts equally + # for the convenient alignment of some circuit graphs. + # My picked value is 16. + # So you will see that I use `:^16` for a center alignment to given string. + # + # After this, we also need set the padding value carefully. + # This value should consider the length of f-string syntax, pre-defined chars and required chars. + # To make sure a pretty showcase both in code and display. + + def __illustrate_response(self, response: Response, index: int) -> None: + """ + Illustrate response item with given response and item. + + :param response: The response to illustrate. + :param index: The zero-based index of the item to illustrate. + """ + # fetch item and device kind from response + item = response[index] + device_kind = response.device_kind + # print header + print( + "Plan {0:<4} Value: {1:<16} Diff: {2} ({3})".format( + index + 1, + self.__to_plan_head_value(item.value, device_kind), + self.__to_plan_head_diff(item.difference, device_kind), + self.__to_plan_head_diff_pct(item.relative_difference), + ) + ) + # print circuit graph + self.__illustrate_circuit(item.circuit, device_kind) + def __illustrate_circuit(self, circuit: Circuit, device_kind: DeviceKind) -> None: match circuit.device_scale: case CircuitDeviceScale.ONE: @@ -328,45 +395,15 @@ class App: case CircuitDeviceScale.THREE: self.__illustrate_three_device_circuit(circuit, device_kind) - def __get_device_unit(self, device_kind: DeviceKind) -> str: - match device_kind: - case DeviceKind.RESISTOR: - return '\u2126' - case DeviceKind.CAPACITOR: - return 'F' - case DeviceKind.INDUCTOR: - return 'H' - - def __to_circult_graph_value(self, value: float, device_kind: DeviceKind) -> str: - # Remove unit and append device unit - return to_human_readable_value(value)[1:] + self.__get_device_unit(device_kind) - - # YYC MARK: - # The function showing circuit graph should be maintained carefully. - # First, we want they are show in console properly, - # And we also want they have good code view. - # - # I notices that the number part of the output of `to_human_readable_value` will only be - # "+999.9999" or "+9.9999e+00". So its maximum of its length is 11, considering the possibility, - # that the absolute value of exponential part is larger than 99, is close to zero. - # After putting the scale unit and device unit together like " nF", - # the whole maximum size of the built string is 14. - # - # So we need pick a larger number and odd number for the space for showing device value, - # because odd value can be divided by two so it can be split as two parts equally - # for the convenient alignment of some circuit graphs. - # My picked value is 16. - # So you will see that I use `:^16` for a center alignment to given string. - # - # After this, we also need set the padding value carefully. - # This value should consider the length of f-string syntax, pre-defined chars and required chars. - # To make sure a pretty showcase both in code and display. - - def __illustrate_one_device_circuit(self, circuit: Circuit, device_kind: DeviceKind) -> None: + def __illustrate_one_device_circuit( + self, circuit: Circuit, device_kind: DeviceKind + ) -> None: dev1 = self.__to_circult_graph_value(circuit.first_device_value, device_kind) print(f"──[{dev1:^16}]──") - def __illustrate_two_device_circuit(self, circuit: Circuit, device_kind: DeviceKind) -> None: + def __illustrate_two_device_circuit( + self, circuit: Circuit, device_kind: DeviceKind + ) -> None: dev1 = self.__to_circult_graph_value(circuit.first_device_value, device_kind) j2 = circuit.second_device_joint dev2 = self.__to_circult_graph_value(circuit.second_device_value, device_kind) @@ -374,12 +411,14 @@ class App: case JointKind.SERIES: print(f"──[{dev1:^16}]──[{dev2:^16}]──") case JointKind.PARALLEL: - SEP0: str = ' ' * (6 + (16 - 10)) + SEP0: str = " " * (6 + (16 - 10)) print(f" ┌──[{dev1:^16}]──┐ ") print(f"──┤ {SEP0} ├──") print(f" └──[{dev2:^16}]──┘ ") - def __illustrate_three_device_circuit(self, circuit: Circuit, device_kind: DeviceKind) -> None: + def __illustrate_three_device_circuit( + self, circuit: Circuit, device_kind: DeviceKind + ) -> None: dev1 = self.__to_circult_graph_value(circuit.first_device_value, device_kind) j2 = circuit.second_device_joint dev2 = self.__to_circult_graph_value(circuit.second_device_value, device_kind) @@ -393,8 +432,8 @@ class App: print(f"──[{dev1:^16}]──[{dev2:^16}]──[{dev3:^16}]──") case JointKind.PARALLEL: # First series then parallel - SEP0: str = '─' * (6 + ((16 - 10) // 2)) - SEP1: str = ' ' * (6 + 2 * (16 - 10)) + SEP0: str = "─" * (6 + ((16 - 10) // 2)) + SEP1: str = " " * (6 + 2 * (16 - 10)) print(f" ┌──[{dev1:^16}]──[{dev2:^16}]──┐ ") print(f"──┤ {SEP1} ├──") print(f" └───{SEP0}[{dev3:^16}]{SEP0}───┘ ") @@ -402,7 +441,7 @@ class App: match j3: case JointKind.SERIES: # First parallel then series - SEP0: str = ' ' * (6 + (16 - 10)) + SEP0: str = " " * (6 + (16 - 10)) print(f" {SEP0} ┌──[{dev1:^16}]──┐ ") print(f"──[{dev1:^16}]──┤ {SEP0} ├──") print(f" {SEP0} └──[{dev2:^16}]──┘ ") @@ -412,6 +451,8 @@ class App: print(f"──┼──[{dev2:^16}]──┼──") print(f" └──[{dev3:^16}]──┘ ") + # endregion + def main() -> None: parser = argparse.ArgumentParser( diff --git a/legacy/src/lcr_connector/dataset.py b/legacy/src/lcr_connector/dataset.py index 566dacf..35aa1df 100644 --- a/legacy/src/lcr_connector/dataset.py +++ b/legacy/src/lcr_connector/dataset.py @@ -217,7 +217,10 @@ def to_human_readable_value(v: float) -> str: case UnitScale.MILLI: return "{:+.4f} m".format(v / 1e-3) case UnitScale.NONE: - return "{:+.4f}".format(v) + # YYC MARK: + # The space of this format string is by design + # for keeping the same style with other format strings. + return "{:+.4f} ".format(v) case UnitScale.KILO: return "{:+.4f} k".format(v / 1e3) case UnitScale.MEGA: diff --git a/legacy/src/lcr_connector/query.py b/legacy/src/lcr_connector/query.py index 323a1e9..0f676a0 100644 --- a/legacy/src/lcr_connector/query.py +++ b/legacy/src/lcr_connector/query.py @@ -16,6 +16,10 @@ class ResponsePriority(enum.Enum): """More accuracy is the first priority.""" +MAX_RESPONSE_CNT: int = 50 +"""The maximum count for the response item count passed in request.""" + + @dataclass class Request: """ @@ -33,6 +37,21 @@ class Request: count_limit: int """The limited count of results.""" + def __post_init__(self): + target_value = self.target_value + if target_value <= 0: + raise ValueError( + f"Invalid value {target_value} for target value in request." + ) + tolerance = self.tolerance + if tolerance < 0: + raise ValueError(f"Invalid value {tolerance} for tolerance in request.") + count_limit = self.count_limit + if count_limit <= 0 or count_limit > MAX_RESPONSE_CNT: + raise ValueError( + f"Too large or too less value {count_limit} for response count limit in request." + ) + class ResponseItem: """ @@ -94,7 +113,9 @@ class ResponseItem: :return: The unsigned difference. """ - return self.__cv_trait.unsigned_difference(self.__circuit, difference=self.difference) + return self.__cv_trait.unsigned_difference( + self.__circuit, difference=self.difference + ) @cached_property def relative_difference(self) -> float: @@ -106,7 +127,9 @@ class ResponseItem: :return: The signed relative difference. """ - return self.__cv_trait.relative_difference(self.__circuit, difference=self.difference) + return self.__cv_trait.relative_difference( + self.__circuit, difference=self.difference + ) @cached_property def unsigned_relative_difference(self) -> float: @@ -137,9 +160,7 @@ class Response: def __init__(self, request: Request, candidates: Iterable[Circuit]) -> None: cv_trait = CircuitValueTrait(request.device_kind, request.target_value) self.__device_kind = request.device_kind - self.__sorted_items = list( - ResponseItem(item, cv_trait) for item in candidates - ) + self.__sorted_items = list(ResponseItem(item, cv_trait) for item in candidates) # Sort by different strategy match request.response_priority: