-
Notifications
You must be signed in to change notification settings - Fork 121
dimm kit: improve error handling, handle deleted output devices #3868
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
base: master
Are you sure you want to change the base?
Changes from all commits
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 |
|---|---|---|
| @@ -1,7 +1,10 @@ | ||
| from dataclasses import dataclass, field | ||
| from typing import Dict, Optional, Tuple, Union | ||
| from control import data | ||
| from control.limiting_value import LoadmanagementLimit | ||
| from helpermodules.constants import NO_ERROR | ||
| from modules.common.configurable_io import ConfigurableIo | ||
| from modules.common.fault_state import FaultStateContext | ||
| from modules.io_actions.controllable_consumers.dimming.api_eebus import DimmingEebus | ||
| from modules.io_actions.controllable_consumers.dimming.api_io import DimmingIo | ||
|
|
||
|
|
@@ -63,47 +66,63 @@ | |
|
|
||
| def setup(self): | ||
| for action in self.actions.values(): | ||
| action.setup() | ||
| io_device = data.data.system_data[f"io{action.config.configuration.io_device}"] | ||
| with FaultStateContext(io_device.fault_state, update_always=False): | ||
|
Comment on lines
+69
to
+70
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sehe ich auch so. Fehlende Geräte sollten mit einer Fehlermeldung abgefangen werden. |
||
| action.setup() | ||
|
|
||
| def dimming_get_import_power_left(self, device: Dict) -> Tuple[Optional[float], LoadmanagementLimit]: | ||
| def dimming_get_import_power_left(self, | ||
| device: Dict[str, Union[int, str]] | ||
| ) -> Tuple[Optional[float], LoadmanagementLimit]: | ||
| for action in self.actions.values(): | ||
| if isinstance(action, (DimmingIo, DimmingEebus)): | ||
| for d in action.config.configuration.devices: | ||
| if device == d: | ||
| return action.dimming_get_import_power_left() | ||
| io_device = data.data.system_data[f"io{action.config.configuration.io_device}"] | ||
| with FaultStateContext(io_device.fault_state, update_always=False): | ||
| if isinstance(action, (DimmingIo, DimmingEebus)): | ||
| for d in action.config.configuration.devices: | ||
| if device == d: | ||
| return action.dimming_get_import_power_left() | ||
| else: | ||
| return None, LoadmanagementLimit(None, None) | ||
|
|
||
| def dimming_set_import_power_left(self, device: Dict, used_power: float) -> Optional[float]: | ||
| def dimming_set_import_power_left(self, | ||
| device: Dict[str, Union[int, str]], used_power: float) -> Optional[float]: | ||
| for action in self.actions.values(): | ||
| if isinstance(action, (DimmingIo, DimmingEebus)): | ||
| for d in action.config.configuration.devices: | ||
| if d == device: | ||
| return action.dimming_set_import_power_left(used_power) | ||
|
|
||
| def dimming_via_direct_control(self, device: Dict) -> Tuple[Optional[float], LoadmanagementLimit]: | ||
| io_device = data.data.system_data[f"io{action.config.configuration.io_device}"] | ||
| with FaultStateContext(io_device.fault_state, update_always=False): | ||
| if isinstance(action, (DimmingIo, DimmingEebus)): | ||
| for d in action.config.configuration.devices: | ||
| if d == device: | ||
| return action.dimming_set_import_power_left(used_power) | ||
|
|
||
| def dimming_via_direct_control(self, | ||
| device: Dict[str, Union[int, str]]) -> Tuple[Optional[float], LoadmanagementLimit]: | ||
| for action in self.actions.values(): | ||
| if isinstance(action, DimmingDirectControl): | ||
| for d in action.config.configuration.devices: | ||
| if device == d: | ||
| return action.dimming_via_direct_control() | ||
| io_device = data.data.system_data[f"io{action.config.configuration.io_device}"] | ||
| with FaultStateContext(io_device.fault_state, update_always=False): | ||
| if isinstance(action, DimmingDirectControl): | ||
| for d in action.config.configuration.devices: | ||
| if device == d: | ||
| return action.dimming_via_direct_control() | ||
| else: | ||
| return None, LoadmanagementLimit(None, None) | ||
|
|
||
| def ripple_control_receiver(self, device: Dict) -> Tuple[float, LoadmanagementLimit]: | ||
| def ripple_control_receiver(self, device: Dict[str, Union[int, str]]) -> Tuple[float, LoadmanagementLimit]: | ||
| for action in self.actions.values(): | ||
| if isinstance(action, RippleControlReceiver): | ||
| for d in action.config.configuration.devices: | ||
| if device == d: | ||
| return action.ripple_control_receiver() | ||
| io_device = data.data.system_data[f"io{action.config.configuration.io_device}"] | ||
| with FaultStateContext(io_device.fault_state, update_always=False): | ||
| if isinstance(action, RippleControlReceiver): | ||
| for d in action.config.configuration.devices: | ||
| if device == d: | ||
| return action.ripple_control_receiver() | ||
| else: | ||
| return 1, LoadmanagementLimit(None, None) | ||
|
|
||
| def stepwise_control(self, device_id: int) -> Tuple[Optional[float], LoadmanagementLimit]: | ||
| for action in self.actions.values(): | ||
| if isinstance(action, (StepwiseControlEebus, StepwiseControlIo)): | ||
| if device_id in [component["id"] for component in action.config.configuration.devices]: | ||
| return action.control_stepwise() | ||
| io_device = data.data.system_data[f"io{action.config.configuration.io_device}"] | ||
| with FaultStateContext(io_device.fault_state, update_always=False): | ||
| if isinstance(action, (StepwiseControlEebus, StepwiseControlIo)): | ||
| if device_id in [component["id"] for component in action.config.configuration.devices]: | ||
| return action.control_stepwise() | ||
| else: | ||
| return None, LoadmanagementLimit(None, None) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,7 @@ | ||||||||||||
| import logging | ||||||||||||
| import traceback | ||||||||||||
| from typing import Optional, Callable, TypeVar | ||||||||||||
| from types import TracebackType | ||||||||||||
| from typing import Optional, Callable, Type, TypeVar | ||||||||||||
|
|
||||||||||||
| from helpermodules import exceptions | ||||||||||||
| from helpermodules.pub import Pub | ||||||||||||
|
|
@@ -87,4 +88,31 @@ def from_exception(self, exception: Optional[Exception] = None) -> None: | |||||||||||
| exception) | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| class FaultStateContext: | ||||||||||||
| def __init__(self, fault_state: FaultState, update_always: bool = True, reraise: bool = False) -> None: | ||||||||||||
| self.__fault_state = fault_state | ||||||||||||
| self.update_always = update_always | ||||||||||||
| self.reraise = reraise | ||||||||||||
|
|
||||||||||||
| def __enter__(self) -> None: | ||||||||||||
| if self.update_always: | ||||||||||||
| self.__fault_state.no_error() | ||||||||||||
| return None | ||||||||||||
|
|
||||||||||||
| def __exit__(self, | ||||||||||||
| exc_type: Optional[Type[BaseException]], | ||||||||||||
| exc_value: Optional[BaseException], | ||||||||||||
| traceback: Optional[TracebackType]) -> bool: | ||||||||||||
| if isinstance(exc_value, Exception): | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wie von Copilot angemerkt, sollten wichtige Systemereignisse nicht verschluckt werden.
Suggested change
|
||||||||||||
| self.__fault_state.from_exception(exc_value) | ||||||||||||
| elif self.update_always is False and self.__fault_state.fault_state == 0: | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
| # Fehlerstatus nicht überschreiben | ||||||||||||
| return True | ||||||||||||
| self.__fault_state.store_error() | ||||||||||||
| if self.reraise is False or exc_value is None: | ||||||||||||
| return True | ||||||||||||
| else: | ||||||||||||
| return False | ||||||||||||
|
Comment on lines
+106
to
+115
|
||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| T_C = TypeVar("T_C", bound=Callable) | ||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,29 @@ | ||
| import logging | ||
| from typing import Dict, Union | ||
|
|
||
| from control import data | ||
| from modules.common.fault_state_level import FaultStateLevel | ||
|
|
||
|
|
||
| control_command_log = logging.getLogger("steuve_control_command") | ||
|
|
||
|
|
||
| def check_fault_state_io_device(io_device: int) -> bool: | ||
| return data.data.io_states[f"io_states{io_device}"].data.get.fault_state == FaultStateLevel.ERROR | ||
|
|
||
|
|
||
| def get_device_log_message(device: Dict[str, Union[int, str]]) -> str: | ||
| try: | ||
| if device["type"] == "cp": | ||
| cp = f"cp{device['id']}" | ||
| return (f"Ladepunkt {data.data.cp_data[cp].data.config.name}: " | ||
| f"{data.data.cp_data[cp].data.get.powers}W, ") | ||
| if device["type"] == "io": | ||
| io = f"io{device['id']}" | ||
| return (f"{data.data.system_data[io].config.name}: " | ||
| "Leistung unbekannt, ") | ||
|
Comment on lines
+19
to
+24
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Da hat er Recht. |
||
| except KeyError: | ||
| control_command_log.warning(f"Zugriff auf gelöschtes Gerät nicht möglich: {device}") | ||
| except Exception: | ||
| control_command_log.exception(f"Fehler beim Zugriff auf Gerät {device}") | ||
| return "Unbekanntes Gerät, " | ||
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.