Skip to content

Commit 06c0db1

Browse files
committed
io action: error of io device use remaining energy, show message
1 parent f7e4e31 commit 06c0db1

File tree

11 files changed

+182
-89
lines changed

11 files changed

+182
-89
lines changed

packages/control/io_device.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from dataclasses import dataclass, field
2-
from typing import Dict, Optional, Union
2+
from typing import Dict, Optional, Tuple, Union
33
from control import data
4-
from control.limiting_value import LimitingValue
4+
from control.limiting_value import LimitingValue, LoadmanagementLimit
55
from helpermodules.constants import NO_ERROR
66
from modules.common.utils.component_parser import get_io_name_by_id
77
from modules.io_actions.controllable_consumers.dimming.api_eebus import DimmingEebus
@@ -67,15 +67,15 @@ def _check_fault_state_io_device(self, io_device: int) -> None:
6767
if data.data.io_states[f"io_states{io_device}"].data.get.fault_state == 2:
6868
raise ValueError(LimitingValue.CONTROLLABLE_CONSUMERS_ERROR.value.format(get_io_name_by_id(io_device)))
6969

70-
def dimming_get_import_power_left(self, device: Dict) -> Optional[float]:
70+
def dimming_get_import_power_left(self, device: Dict) -> Tuple[Optional[float], LoadmanagementLimit]:
7171
for action in self.actions.values():
7272
if isinstance(action, (DimmingIo, DimmingEebus)):
7373
for d in action.config.configuration.devices:
7474
if device == d:
7575
self._check_fault_state_io_device(action.config.configuration.io_device)
7676
return action.dimming_get_import_power_left()
7777
else:
78-
return None
78+
return None, LoadmanagementLimit(None, None)
7979

8080
def dimming_set_import_power_left(self, device: Dict, used_power: float) -> Optional[float]:
8181
for action in self.actions.values():
@@ -84,31 +84,31 @@ def dimming_set_import_power_left(self, device: Dict, used_power: float) -> Opti
8484
if d == device:
8585
return action.dimming_set_import_power_left(used_power)
8686

87-
def dimming_via_direct_control(self, device: Dict) -> Optional[float]:
87+
def dimming_via_direct_control(self, device: Dict) -> Tuple[Optional[float], LoadmanagementLimit]:
8888
for action in self.actions.values():
8989
if isinstance(action, DimmingDirectControl):
9090
for d in action.config.configuration.devices:
9191
if device == d:
9292
self._check_fault_state_io_device(action.config.configuration.io_device)
9393
return action.dimming_via_direct_control()
9494
else:
95-
return None
95+
return None, LoadmanagementLimit(None, None)
9696

97-
def ripple_control_receiver(self, device: Dict) -> float:
97+
def ripple_control_receiver(self, device: Dict) -> Tuple[float, LoadmanagementLimit]:
9898
for action in self.actions.values():
9999
if isinstance(action, RippleControlReceiver):
100100
for d in action.config.configuration.devices:
101101
if device == d:
102102
self._check_fault_state_io_device(action.config.configuration.io_device)
103103
return action.ripple_control_receiver()
104104
else:
105-
return 1
105+
return 1, LoadmanagementLimit(None, None)
106106

107-
def stepwise_control(self, device_id: int) -> Optional[float]:
107+
def stepwise_control(self, device_id: int) -> Tuple[Optional[float], LoadmanagementLimit]:
108108
for action in self.actions.values():
109109
if isinstance(action, (StepwiseControlEebus, StepwiseControlIo)):
110110
if device_id in [component["id"] for component in action.config.configuration.devices]:
111111
self._check_fault_state_io_device(action.config.configuration.io_device)
112112
return action.control_stepwise()
113113
else:
114-
return None
114+
return None, LoadmanagementLimit(None, None)

packages/control/limiting_value.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ class LimitingValue(Enum):
1111
DIMMING_VIA_DIRECT_CONTROL = ", da die Dimmung per Direkt-Steuerung die Ladeleistung auf 4,2 kW begrenzt."
1212
RIPPLE_CONTROL_RECEIVER = (", da der Ladepunkt durch den RSE-Kontakt auf {}% der konfigurierten Anschlussleistung "
1313
"reduziert wird.")
14+
CONTROL_STEPWISE = "Leistung begrenzt auf {}%"
1415
CONTROLLABLE_CONSUMERS_ERROR = (", da aufgrund eines Fehlers im IO-Gerät {} die steuerbaren Verbraucher nicht "
1516
"gesteuert werden können. Bitte prüfe die Status-Seite.")
17+
MISSING_CONFIFGURATION = ", da die Konfiguration für die Aktion unvollständig ist."
1618

1719

1820
@dataclass

packages/control/loadmanagement.py

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -137,35 +137,31 @@ def _limit_by_current(self,
137137
def _limit_by_dimming_via_direct_control(self,
138138
missing_currents: List[float],
139139
cp: Chargepoint) -> Tuple[List[float], LoadmanagementLimit]:
140-
if data.data.io_actions.dimming_via_direct_control({"type": "cp", "id": cp.num}):
140+
value, limit = data.data.io_actions.dimming_via_direct_control({"type": "cp", "id": cp.num})
141+
if value is not None:
141142
phases = 3-missing_currents.count(0)
142143
current_per_phase = 4200 / 230 / phases
143144
available_currents = [current_per_phase -
144145
cp.data.set.target_current if c > 0 else 0 for c in missing_currents]
145146
log.debug(f"Dimmung per Direkt-Steuerung: {available_currents}A")
146-
limit = LoadmanagementLimit(LimitingValue.DIMMING_VIA_DIRECT_CONTROL.value,
147-
LimitingValue.DIMMING_VIA_DIRECT_CONTROL)
148-
return available_currents, limit
149-
else:
150-
return missing_currents, LoadmanagementLimit(None, None)
147+
return available_currents, limit
151148

152149
def _limit_by_dimming(self,
153150
available_currents: List[float],
154151
cp: Chargepoint) -> Tuple[List[float], LoadmanagementLimit]:
155-
dimming_power_left = data.data.io_actions.dimming_get_import_power_left({"type": "cp", "id": cp.num})
152+
dimming_power_left, limit = data.data.io_actions.dimming_get_import_power_left({"type": "cp", "id": cp.num})
156153
if dimming_power_left:
157154
if sum(available_currents)*230 > dimming_power_left:
158155
phases = 3-available_currents.count(0)
159156
overload_per_phase = (sum(available_currents) - dimming_power_left/230)/phases
160157
available_currents = [c - overload_per_phase if c > 0 else 0 for c in available_currents]
161158
log.debug(f"Reduzierung der Ströme durch die Dimmung: {available_currents}A")
162-
return available_currents, LoadmanagementLimit(LimitingValue.DIMMING.value, LimitingValue.DIMMING)
163-
return available_currents, LoadmanagementLimit(None, None)
159+
return available_currents, limit
164160

165161
def _limit_by_ripple_control_receiver(self,
166162
available_currents: List[float],
167163
cp: Chargepoint) -> Tuple[List[float], LoadmanagementLimit]:
168-
value = data.data.io_actions.ripple_control_receiver({"type": "cp", "id": cp.num})
164+
value, limit = data.data.io_actions.ripple_control_receiver({"type": "cp", "id": cp.num})
169165
if value != 1:
170166
phases = 3-available_currents.count(0)
171167
if phases > 1:
@@ -177,9 +173,4 @@ def _limit_by_ripple_control_receiver(self,
177173
available_currents = [min(max_current*value - cp.data.set.target_current, c)
178174
if c > 0 else 0 for c in available_currents]
179175
log.debug(f"Reduzierung durch RSE-Kontakt auf {value*100}%, maximal {max_current*value}A")
180-
limit = LoadmanagementLimit(
181-
LimitingValue.RIPPLE_CONTROL_RECEIVER.value.format(value*100),
182-
LimitingValue.RIPPLE_CONTROL_RECEIVER)
183-
return available_currents, limit
184-
else:
185-
return available_currents, LoadmanagementLimit(None, None)
176+
return available_currents, limit

packages/control/pv_all.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,11 @@ def calc_power_for_all_components(self) -> None:
6969
else:
7070
if fault_state < module.data.get.fault_state:
7171
fault_state = module.data.get.fault_state
72-
limit_value = data.data.io_actions.stepwise_control(module.num)
73-
if limit_value is not None and module.data.get.fault_state == 0:
74-
msg = (
75-
f"Leistung begrenzt auf {int(limit_value * 100)}%"
76-
if limit_value < 1
77-
else "Keine Leistungsbegrenzung aktiv."
78-
)
79-
module.data.get.fault_str = msg
80-
Pub().pub(f"openWB/set/pv/{module.num}/get/fault_str", msg)
72+
limit = data.data.io_actions.stepwise_control(module.num)[1]
73+
if module.data.get.fault_state == 0:
74+
# Fehlermeldung nicht überschreiben
75+
module.data.get.fault_str = limit.message
76+
Pub().pub(f"openWB/set/pv/{module.num}/get/fault_str", limit.message)
8177
except Exception:
8278
log.exception(f"Fehler im allgemeinen PV-Modul für pv{module.num}")
8379
if fault_state == 0:
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from control import data
2+
3+
4+
def check_fault_state_io_device(io_device: int) -> bool:
5+
return data.data.io_states[f"io_states{io_device}"].data.get.fault_state == 2

packages/modules/io_actions/controllable_consumers/dimming/api_eebus.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import logging
2+
from typing import Optional, Tuple
23
from control import data
4+
from control.limiting_value import LimitingValue, LoadmanagementLimit
35
from helpermodules import timecheck
46
from helpermodules.logger import ModifyLoglevelContext
57
from helpermodules.pub import Pub
68
from helpermodules.timecheck import create_timestamp
79
from dataclass_utils import asdict
810
from modules.common.abstract_io import AbstractIoAction
11+
from modules.common.utils.component_parser import get_io_name_by_id
12+
from modules.io_actions.common import check_fault_state_io_device
913
from modules.io_actions.controllable_consumers.dimming.config import DimmingSetup
1014
from modules.io_devices.eebus.config import AnalogInputMapping, DigitalInputMapping
1115

@@ -42,9 +46,12 @@ def setup(self) -> None:
4246
log.debug(f"Dimmen: {self.import_power_left}W inkl. Überschuss")
4347

4448
with ModifyLoglevelContext(control_command_log, logging.DEBUG):
45-
if self.dimming_active():
49+
if self.dimming_active() or check_fault_state_io_device(self.config.configuration.io_device):
4650
if self.timestamp is None:
4751
Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", create_timestamp())
52+
if check_fault_state_io_device(self.config.configuration.io_device):
53+
control_command_log.info(
54+
"Fehler des IO-Geräts: Dimmen aktiviert für Failsafe-Modus.")
4855
control_command_log.info(f"Dimmen aktiviert. Übermittelter LPC-Wert: {lpc_value/1000}kWh. "
4956
"Leistungswerte vor Ausführung des Steuerbefehls:")
5057

@@ -64,11 +71,16 @@ def setup(self) -> None:
6471
Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", None)
6572
control_command_log.info("Dimmen deaktiviert.")
6673

67-
def dimming_get_import_power_left(self) -> None:
74+
def dimming_get_import_power_left(self) -> Tuple[Optional[float], LoadmanagementLimit]:
75+
if check_fault_state_io_device(self.config.configuration.io_device):
76+
return (self.import_power_left, LoadmanagementLimit(
77+
LimitingValue.CONTROLLABLE_CONSUMERS_ERROR.value.format(get_io_name_by_id(
78+
self.config.configuration.io_device)),
79+
LimitingValue.CONTROLLABLE_CONSUMERS_ERROR))
6880
if self.dimming_active():
69-
return self.import_power_left
81+
return self.import_power_left, LoadmanagementLimit(LimitingValue.DIMMING.value, LimitingValue.DIMMING)
7082
else:
71-
return None
83+
return None, LoadmanagementLimit(None, None)
7284

7385
def dimming_set_import_power_left(self, used_power: float) -> None:
7486
self.import_power_left -= used_power

packages/modules/io_actions/controllable_consumers/dimming/api_io.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import logging
2+
from typing import Optional, Tuple
23

34
from control import data
5+
from control.limiting_value import LimitingValue, LoadmanagementLimit
46
from helpermodules.logger import ModifyLoglevelContext
57
from helpermodules.pub import Pub
68
from helpermodules.timecheck import create_timestamp
79
from dataclass_utils import asdict
810
from modules.common.abstract_io import AbstractIoAction
11+
from modules.common.utils.component_parser import get_io_name_by_id
12+
from modules.io_actions.common import check_fault_state_io_device
913
from modules.io_actions.controllable_consumers.dimming.config import DimmingSetup
1014

1115
log = logging.getLogger(__name__)
@@ -49,10 +53,11 @@ def setup(self) -> None:
4953
log.debug(f"Dimmen: {self.import_power_left}W inkl. Überschuss")
5054

5155
with ModifyLoglevelContext(control_command_log, logging.DEBUG):
52-
if data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[
53-
self.dimming_input] == self.dimming_value:
56+
if self.dimming_active() or check_fault_state_io_device(self.config.configuration.io_device):
5457
if self.timestamp is None:
5558
Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", create_timestamp())
59+
if check_fault_state_io_device(self.config.configuration.io_device):
60+
control_command_log.info("Fehler des IO-Geräts: Dimmen aktiviert für Failsafe-Modus.")
5661
control_command_log.info("Dimmen aktiviert. Leistungswerte vor Ausführung des Steuerbefehls:")
5762

5863
msg = (f"EVU-Zähler: "
@@ -71,12 +76,17 @@ def setup(self) -> None:
7176
Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", None)
7277
control_command_log.info("Dimmen deaktiviert.")
7378

74-
def dimming_get_import_power_left(self) -> None:
79+
def dimming_get_import_power_left(self) -> Tuple[Optional[float], LoadmanagementLimit]:
80+
if check_fault_state_io_device(self.config.configuration.io_device):
81+
return (self.import_power_left, LoadmanagementLimit(
82+
LimitingValue.CONTROLLABLE_CONSUMERS_ERROR.value.format(get_io_name_by_id(
83+
self.config.configuration.io_device)),
84+
LimitingValue.CONTROLLABLE_CONSUMERS_ERROR))
7585
if self.dimming_active():
76-
return self.import_power_left
86+
return self.import_power_left, LoadmanagementLimit(LimitingValue.DIMMING.value, LimitingValue.DIMMING)
7787
elif data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[
7888
self.no_dimming_input] == self.no_dimming_value:
79-
return None
89+
return None, LoadmanagementLimit(None, None)
8090
else:
8191
raise Exception("Pattern passt nicht zur Dimmung.")
8292

packages/modules/io_actions/controllable_consumers/dimming_direct_control/api.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import logging
2+
from typing import Optional, Tuple
23
from control import data
4+
from control.limiting_value import LimitingValue, LoadmanagementLimit
35
from helpermodules.logger import ModifyLoglevelContext
46
from helpermodules.pub import Pub
57
from helpermodules.timecheck import create_timestamp
68
from modules.common.abstract_device import DeviceDescriptor
79
from modules.common.abstract_io import AbstractIoAction
10+
from modules.common.utils.component_parser import get_io_name_by_id
11+
from modules.io_actions.common import check_fault_state_io_device
812
from modules.io_actions.controllable_consumers.dimming_direct_control.config import DimmingDirectControlSetup
913

1014
control_command_log = logging.getLogger("steuve_control_command")
@@ -29,13 +33,16 @@ def __init__(self, config: DimmingDirectControlSetup):
2933
def setup(self) -> None:
3034
with ModifyLoglevelContext(control_command_log, logging.DEBUG):
3135
if data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[
32-
self.dimming_input] == self.dimming_value:
36+
self.dimming_input] == self.dimming_value or check_fault_state_io_device(self.config.configuration.io_device):
3337
device = self.config.configuration.devices[0]
3438
if device["type"] == "cp":
3539
cp = f"cp{device['id']}"
3640
if self.timestamp is None:
3741
Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", create_timestamp())
3842
if device["type"] == "cp":
43+
if check_fault_state_io_device(self.config.configuration.io_device):
44+
control_command_log.info(
45+
"Fehler des IO-Geräts: Direktsteuerung an Ladepunkt aktiviert für Failsafe-Modus.")
3946
control_command_log.info(
4047
f"Direktsteuerung an Ladepunkt "
4148
f"{data.data.cp_data[cp].data.config.name} aktiviert. "
@@ -55,13 +62,19 @@ def setup(self) -> None:
5562
Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", None)
5663
control_command_log.info("Direktsteuerung deaktiviert.")
5764

58-
def dimming_via_direct_control(self) -> None:
59-
if data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[
65+
def dimming_via_direct_control(self) -> Tuple[Optional[float], LoadmanagementLimit]:
66+
if check_fault_state_io_device(self.config.configuration.io_device):
67+
return (4200, LoadmanagementLimit(
68+
LimitingValue.CONTROLLABLE_CONSUMERS_ERROR.value.format(get_io_name_by_id(
69+
self.config.configuration.io_device)),
70+
LimitingValue.CONTROLLABLE_CONSUMERS_ERROR))
71+
elif data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[
6072
self.dimming_input] == self.dimming_value:
61-
return 4200
73+
return (4200, LoadmanagementLimit(LimitingValue.DIMMING_VIA_DIRECT_CONTROL.value,
74+
LimitingValue.DIMMING_VIA_DIRECT_CONTROL))
6275
elif data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[
6376
self.no_dimming_input] == self.no_dimming_value:
64-
return None
77+
return None, LoadmanagementLimit(None, None)
6578
else:
6679
raise Exception("Pattern passt nicht zur Dimmung per Direktsteuerung.")
6780

0 commit comments

Comments
 (0)