Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/control/algorithm/additional_current.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ def set_additional_current(self) -> None:
available_currents, limit = Loadmanagement().get_available_currents(missing_currents, counter, cp)
log.debug(f"cp {cp.num} available currents {available_currents} missing currents "
f"{missing_currents} limit {limit.message}")
cp.data.control_parameter.limit = limit
if limit.limiting_value is not None:
cp.data.control_parameter.limit = limit
available_for_cp = common.available_current_for_cp(cp, counts, available_currents, missing_currents)
current = common.get_current_to_set(
cp.data.set.current, available_for_cp, cp.data.set.target_current)
Expand Down
3 changes: 2 additions & 1 deletion packages/control/algorithm/min_current.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
available_for_cp = common.available_current_for_cp(
cp, counts, available_currents, missing_currents)
current = common.get_current_to_set(
Expand Down
4 changes: 3 additions & 1 deletion packages/control/algorithm/surplus_controlled.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
available_for_cp = common.available_current_for_cp(cp, counts, available_currents, missing_currents)
if counter.get_control_range_state(feed_in_yield) == ControlRangeState.MIDDLE:
pv_charging = data.data.general_data.data.chargemode_config.pv_charging
Expand Down
5 changes: 3 additions & 2 deletions packages/control/ev/ev.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,9 @@ def _check_phase_switch_conditions(self,
all_surplus = data.data.counter_all_data.get_evu_counter().get_usable_surplus(feed_in_yield)
required_surplus = control_parameter.min_current * max_phases_ev * 230 - get_power
unbalanced_load_limit_reached = limit.limiting_value == LimitingValue.UNBALANCED_LOAD
condition_1_to_3 = (((get_medium_charging_current(get_currents) > max_current_range and
all_surplus > required_surplus) or unbalanced_load_limit_reached) and
current_limit_reached = limit.limiting_value == LimitingValue.CURRENT
condition_1_to_3 = ((((get_medium_charging_current(get_currents) > max_current_range or current_limit_reached)
and all_surplus > required_surplus) or unbalanced_load_limit_reached) and
phases_in_use == 1)
condition_3_to_1 = get_medium_charging_current(
get_currents) < min_current_range and all_surplus <= 0 and phases_in_use > 1
Expand Down
36 changes: 36 additions & 0 deletions packages/dataclass_utils/_dataclass_asdict_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest

from dataclass_utils import asdict
from dataclass_utils.conftest import MyDataclass


class SingleValue:
Expand Down Expand Up @@ -37,3 +38,38 @@ def test_asdict(object, expected_dict: dict):

# evaluation
assert actual == expected_dict


MY_DATACLASS_AS_DICT = {
"str_value": "string_value",
"float_value": 5.2,
"int_value": 6,
"enum_value": "value1",
"nested_dataclass": {
"nested_str": "nested string",
"nested_int": 42
},
"nested_dataclass_enum_value": {
"D1": "value1",
"D2": "value2"
},
"dict_of_dataclass_value": {"a": {"nested_int": 42,
"nested_str": "nested string"},
"b": {"nested_int": 42,
"nested_str": "nested string"}},
"dict_value": {"a": "a", "b": 2},
"dict2_value": {"a": 1, "b": 2},
"list_value": ["a", 2, None],
"list2_value": ["a", 2, None],
# JSON kennt keine Tupel
"tuple_value": [None, "a", 2],
"tuple2_value": [None, "a", 2]
}


def test_dataclass_as_dict():
# execution
actual_dict = asdict(MyDataclass())

# evaluation
assert actual_dict == MY_DATACLASS_AS_DICT
41 changes: 20 additions & 21 deletions packages/dataclass_utils/_dataclass_from_dict.py
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')
Expand All @@ -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
Expand All @@ -46,26 +46,25 @@ 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 isinstance(args[1], type(None)):
actual_type = args[0] # Extract X from Optional[X]

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
31 changes: 31 additions & 0 deletions packages/dataclass_utils/_dataclass_from_dict_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import pytest

from dataclass_utils import dataclass_from_dict
from dataclass_utils.conftest import MyDataclass

T = TypeVar('T')

Expand Down Expand Up @@ -125,3 +126,33 @@ def test_from_dict_without_optional():
# evaluation
assert actual.a == "aValue"
assert actual.o is None


MY_DATACLASS_AS_DICT = {
"str_value": "string_value",
"float_value": 5.2,
"int_value": 6,
"enum_value": "value1",
"nested_dataclass": {
"nested_str": "nested string",
"nested_int": 42
},
"nested_dataclass_enum_value": {
"D1": "value1",
"D2": "value2"
},
"dict_value": {"a": "a", "b": 2},
"dict2_value": {"a": 1, "b": 2},
"list_value": ["a", 2, None],
"list2_value": ["a", 2, None],
"tuple_value": (None, "a", 2),
"tuple2_value": (None, "a", 2)
}


def test_dataclass_from_dict():
# execution
actual_dict = dataclass_from_dict(MyDataclass, MY_DATACLASS_AS_DICT)

# evaluation
assert vars(actual_dict) == vars(MyDataclass())
46 changes: 46 additions & 0 deletions packages/dataclass_utils/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, List, Tuple


class EnumValues(Enum):
VALUE1 = "value1"
VALUE2 = "value2"


@dataclass
class DataclassEnumValue():
D1: EnumValues = EnumValues.VALUE1
D2: EnumValues = EnumValues.VALUE2


def dataclass_enum_value_factory() -> DataclassEnumValue:
return DataclassEnumValue()


@dataclass
class NestedDataclass:
nested_str: str = "nested string"
nested_int: int = 42


def nested_dataclass_factory() -> NestedDataclass:
return NestedDataclass()


@dataclass
class MyDataclass:
str_value: str = "string_value"
float_value: float = 5.2
int_value: int = 6
enum_value: EnumValues = EnumValues.VALUE1
nested_dataclass: NestedDataclass = field(default_factory=nested_dataclass_factory)
nested_dataclass_enum_value: DataclassEnumValue = field(default_factory=dataclass_enum_value_factory)
dict_of_dataclass_value: Dict[str, NestedDataclass] = field(
default_factory=lambda: {"a": NestedDataclass(), "b": NestedDataclass()})
dict_value: Dict = field(default_factory=lambda: {"a": "a", "b": 2})
dict2_value: dict = field(default_factory=lambda: {"a": 1, "b": 2})
list_value: List = field(default_factory=lambda: ["a", 2, None])
list2_value: list = field(default_factory=lambda: ["a", 2, None])
tuple_value: tuple = field(default_factory=lambda: (None, "a", 2))
tuple2_value: Tuple = field(default_factory=lambda: (None, "a", 2))
Loading