Repository navigation
se automatic phase switch, enum handling with dataclass <->dict conversion #3241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
944d0a7
e7e0760
27c0a9f
6b7d349
d47a6e3
e204b73
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -27,7 +27,8 @@ def set_min_current(self) -> None: | |||||||||
| if max(missing_currents) > 0: | ||||||||||
| available_currents, limit = Loadmanagement().get_available_currents( | ||||||||||
| missing_currents, counter, cp) | ||||||||||
| cp.data.control_parameter.limit = limit | ||||||||||
| if limit.limiting_value is not None: | ||||||||||
| cp.data.control_parameter.limit = limit | ||||||||||
|
||||||||||
| cp.data.control_parameter.limit = limit | |
| cp.data.control_parameter.limit = limit | |
| else: | |
| cp.data.control_parameter.limit = None |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -61,7 +61,9 @@ def _set(self, | |||||||||||||
| cp, | ||||||||||||||
| feed_in=feed_in_yield | ||||||||||||||
| ) | ||||||||||||||
| cp.data.control_parameter.limit = limit | ||||||||||||||
| # im PV-Laden wird der Strom immer durch die Leistung begrenzt | ||||||||||||||
| if limit.limiting_value is not None and limit.limiting_value != LimitingValue.POWER: | ||||||||||||||
| cp.data.control_parameter.limit = limit | ||||||||||||||
|
||||||||||||||
| cp.data.control_parameter.limit = limit | |
| cp.data.control_parameter.limit = limit | |
| else: | |
| # Clear any stale CURRENT/UNBALANCED limit when there is no limiting | |
| # or when limiting is by POWER (ignored in PV mode). | |
| cp.data.control_parameter.limit = None |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,6 @@ | ||
| from enum import Enum | ||
| import inspect | ||
| from inspect import FullArgSpec, isclass | ||
| import typing | ||
| from typing import TypeVar, Type, Union, get_args, get_origin | ||
|
|
||
| T = TypeVar('T') | ||
|
|
@@ -20,8 +19,9 @@ def dataclass_from_dict(cls: Type[T], args: Union[dict, T]) -> T: | |
| if isinstance(args, cls): | ||
| return args | ||
| elif get_origin(cls): | ||
| # Generische Typen wie Dict[int, float] | ||
| if isinstance(args, get_origin(cls)): | ||
| # Generische Typen wie Dict[int, float] - aber nicht Union, da isinstance mit Union fehlschlägt | ||
| origin = get_origin(cls) | ||
| if origin != Union and isinstance(args, origin): | ||
| return args | ||
| elif isinstance(args, type(cls)): | ||
| return args | ||
|
|
@@ -46,26 +46,27 @@ def _get_argument_value(arg_spec: FullArgSpec, index: int, parameters: dict): | |
|
|
||
|
|
||
| def _dataclass_from_dict_recurse(value, requested_type: Type[T]): | ||
| if get_origin(requested_type) == list: | ||
| # Handle Optional types (Union[X, None]) - extract the actual type | ||
| actual_type = requested_type | ||
| if get_origin(requested_type) == Union: | ||
| args = get_args(requested_type) | ||
| if len(args) == 2 and args[1].__name__ == 'NoneType': | ||
| if value is None: | ||
| return None | ||
| actual_type = args[0] # Extract X from Optional[X] | ||
|
Comment on lines
21
to
+56
|
||
|
|
||
| if get_origin(actual_type) == list: | ||
| # Extrahiere den generischen Typ der Liste | ||
| if get_args(requested_type): | ||
| generic_type = get_args(requested_type)[0] | ||
| if get_args(actual_type): | ||
| generic_type = get_args(actual_type)[0] | ||
| # Konvertiere jedes Element der Liste in den generischen Typ | ||
| return [_dataclass_from_dict_recurse(item, generic_type) for item in value] | ||
|
|
||
| if isinstance(value, dict) and not ( | ||
| _is_optional_of_dict(requested_type) or | ||
| issubclass(requested_type if isclass(requested_type) else type(bool), dict)): | ||
| return dataclass_from_dict(requested_type, value) | ||
| if isinstance(requested_type, type) and issubclass(requested_type, Enum): | ||
| return requested_type(value) | ||
| return value | ||
|
|
||
| # Handle dict types (both direct and Optional[dict]) | ||
| if isinstance(value, dict) and isclass(actual_type) and not issubclass(actual_type, dict): | ||
| return dataclass_from_dict(actual_type, value) | ||
|
|
||
| def _is_optional_of_dict(requested_type): | ||
| # Optional[dict] is an alias for Union[dict, None] | ||
| if typing.get_origin(requested_type) == Union: | ||
| args = typing.get_args(requested_type) | ||
| if len(args) == 2: | ||
| return issubclass(args[0], dict) and issubclass(args[1], type(None)) | ||
| return False | ||
| # Handle Enum types (both direct and Optional[Enum]) | ||
| if isinstance(actual_type, type) and issubclass(actual_type, Enum): | ||
| return actual_type(value) | ||
| return value | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue as in
min_current.py: skipping the assignment whenlimit.limiting_value is Nonemeanscp.data.control_parameter.limitcan remain set to an old limiting reason even after the constraint is gone. That stale state can propagate into phase-switch logic and status messaging. Consider clearing/resetting the limit explicitly when there is no active limitation.