epyt-flow 0.9.0__py3-none-any.whl → 0.10.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- epyt_flow/VERSION +1 -1
- epyt_flow/gym/scenario_control_env.py +9 -1
- epyt_flow/serialization.py +4 -0
- epyt_flow/simulation/events/sensor_reading_attack.py +16 -3
- epyt_flow/simulation/events/sensor_reading_event.py +18 -3
- epyt_flow/simulation/scada/__init__.py +3 -1
- epyt_flow/simulation/scada/advanced_control.py +6 -2
- epyt_flow/simulation/scada/complex_control.py +625 -0
- epyt_flow/simulation/scada/custom_control.py +134 -0
- epyt_flow/simulation/scada/scada_data.py +20 -3
- epyt_flow/simulation/scada/simple_control.py +317 -0
- epyt_flow/simulation/scenario_config.py +123 -23
- epyt_flow/simulation/scenario_simulator.py +394 -23
- epyt_flow/simulation/sensor_config.py +16 -0
- {epyt_flow-0.9.0.dist-info → epyt_flow-0.10.0.dist-info}/LICENSE +1 -1
- {epyt_flow-0.9.0.dist-info → epyt_flow-0.10.0.dist-info}/METADATA +7 -6
- {epyt_flow-0.9.0.dist-info → epyt_flow-0.10.0.dist-info}/RECORD +19 -16
- {epyt_flow-0.9.0.dist-info → epyt_flow-0.10.0.dist-info}/WHEEL +1 -1
- {epyt_flow-0.9.0.dist-info → epyt_flow-0.10.0.dist-info}/top_level.txt +0 -0
epyt_flow/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.10.0
|
|
@@ -107,6 +107,14 @@ class ScenarioControlEnv(ABC):
|
|
|
107
107
|
Current SCADA data (i.e. sensor readings).
|
|
108
108
|
"""
|
|
109
109
|
if self._scenario_sim is not None:
|
|
110
|
+
# Abort current simulation if any is runing
|
|
111
|
+
try:
|
|
112
|
+
next(self._sim_generator)
|
|
113
|
+
self._sim_generator.send(True)
|
|
114
|
+
except StopIteration:
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
# Close scenario
|
|
110
118
|
self._scenario_sim.close()
|
|
111
119
|
|
|
112
120
|
self._scenario_sim = ScenarioSimulator(
|
|
@@ -194,7 +202,7 @@ class ScenarioControlEnv(ABC):
|
|
|
194
202
|
if pattern_idx == 0:
|
|
195
203
|
warnings.warn(f"No pattern for pump '{pump_id}' found -- a new pattern is created")
|
|
196
204
|
pattern_idx = self._scenario_sim.epanet_api.addPattern(f"pump_speed_{pump_id}")
|
|
197
|
-
self._scenario_sim.epanet_api.setLinkPumpPatternIndex(pump_idx, pattern_idx)
|
|
205
|
+
self._scenario_sim.epanet_api.setLinkPumpPatternIndex(pump_idx + 1, pattern_idx)
|
|
198
206
|
|
|
199
207
|
self._scenario_sim.epanet_api.setPattern(pattern_idx, np.array([speed]))
|
|
200
208
|
|
epyt_flow/serialization.py
CHANGED
|
@@ -48,6 +48,10 @@ PUMP_STATE_EVENT_ID = 28
|
|
|
48
48
|
PUMP_SPEED_EVENT_ID = 29
|
|
49
49
|
VALVE_STATE_EVENT_ID = 30
|
|
50
50
|
SPECIESINJECTION_EVENT_ID = 31
|
|
51
|
+
SIMPLE_CONTROL_ID = 32
|
|
52
|
+
COMPLEX_CONTROL_ID = 33
|
|
53
|
+
COMPLEX_CONTROL_CONDITION_ID = 34
|
|
54
|
+
COMPLEX_CONTROL_ACTION_ID = 35
|
|
51
55
|
|
|
52
56
|
|
|
53
57
|
def my_packb(data: Any) -> bytes:
|
|
@@ -46,7 +46,7 @@ class SensorOverrideAttack(SensorReadingAttack, JsonSerializable):
|
|
|
46
46
|
@property
|
|
47
47
|
def new_sensor_values(self) -> np.ndarray:
|
|
48
48
|
"""
|
|
49
|
-
|
|
49
|
+
Returns the new sensor reading values -- i.e. these values replace the
|
|
50
50
|
true sensor reading values.
|
|
51
51
|
|
|
52
52
|
Returns
|
|
@@ -64,7 +64,7 @@ class SensorOverrideAttack(SensorReadingAttack, JsonSerializable):
|
|
|
64
64
|
raise TypeError("Can not compare 'SensorOverrideAttack' instance " +
|
|
65
65
|
f"with '{type(other)}' instance")
|
|
66
66
|
|
|
67
|
-
return super().__eq__(other) and self.__new_sensor_values == other.new_sensor_values
|
|
67
|
+
return super().__eq__(other) and np.all(self.__new_sensor_values == other.new_sensor_values)
|
|
68
68
|
|
|
69
69
|
def __str__(self) -> str:
|
|
70
70
|
return f"{type(self).__name__} {super().__str__()} " +\
|
|
@@ -151,6 +151,19 @@ class SensorReplayAttack(SensorReadingAttack, JsonSerializable):
|
|
|
151
151
|
"""
|
|
152
152
|
return self.__sensor_data_time_window_end
|
|
153
153
|
|
|
154
|
+
@property
|
|
155
|
+
def new_sensor_values(self) -> np.ndarray:
|
|
156
|
+
"""
|
|
157
|
+
Returns the new sensor reading values -- i.e. these values replace the
|
|
158
|
+
true sensor reading values.
|
|
159
|
+
|
|
160
|
+
Returns
|
|
161
|
+
-------
|
|
162
|
+
`np.ndarray`
|
|
163
|
+
New sensor readings.
|
|
164
|
+
"""
|
|
165
|
+
return deepcopy(self.__new_sensor_values)
|
|
166
|
+
|
|
154
167
|
def get_attributes(self) -> dict:
|
|
155
168
|
my_attributes = {"new_sensor_values": self.__new_sensor_values,
|
|
156
169
|
"replay_data_time_window_start": self.__sensor_data_time_window_start,
|
|
@@ -163,7 +176,7 @@ class SensorReplayAttack(SensorReadingAttack, JsonSerializable):
|
|
|
163
176
|
raise TypeError("Can not compare 'SensorReplayAttack' instance " +
|
|
164
177
|
f"with '{type(other)}' instance")
|
|
165
178
|
|
|
166
|
-
return super().__eq__(other) and self.__new_sensor_values == other.new_sensor_values
|
|
179
|
+
return super().__eq__(other) and np.all(self.__new_sensor_values == other.new_sensor_values)
|
|
167
180
|
|
|
168
181
|
def __str__(self) -> str:
|
|
169
182
|
return f"{type(self).__name__} {super().__str__()} " +\
|
|
@@ -95,13 +95,28 @@ class SensorReadingEvent(Event):
|
|
|
95
95
|
if self.__sensor_id not in sensor_config.tank_volume_sensors:
|
|
96
96
|
__show_warning()
|
|
97
97
|
elif self.__sensor_type == SENSOR_TYPE_NODE_BULK_SPECIES:
|
|
98
|
-
|
|
98
|
+
sensor_present = False
|
|
99
|
+
for _, sensors_id in sensor_config.bulk_species_node_sensors.items():
|
|
100
|
+
if self.__sensor_id in sensors_id:
|
|
101
|
+
sensor_present = True
|
|
102
|
+
break
|
|
103
|
+
if sensor_present is False:
|
|
99
104
|
__show_warning()
|
|
100
105
|
elif self.__sensor_type == SENSOR_TYPE_LINK_BULK_SPECIES:
|
|
101
|
-
|
|
106
|
+
sensor_present = False
|
|
107
|
+
for _, sensors_id in sensor_config.bulk_species_link_sensors.items():
|
|
108
|
+
if self.__sensor_id in sensors_id:
|
|
109
|
+
sensor_present = True
|
|
110
|
+
break
|
|
111
|
+
if sensor_present is False:
|
|
102
112
|
__show_warning()
|
|
103
113
|
elif self.__sensor_type == SENSOR_TYPE_SURFACE_SPECIES:
|
|
104
|
-
|
|
114
|
+
sensor_present = False
|
|
115
|
+
for _, sensors_id in sensor_config.surface_species_sensors.items():
|
|
116
|
+
if self.__sensor_id in sensors_id:
|
|
117
|
+
sensor_present = True
|
|
118
|
+
break
|
|
119
|
+
if sensor_present is False:
|
|
105
120
|
__show_warning()
|
|
106
121
|
|
|
107
122
|
@property
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Deprecated -- use epyt_flow.simulation.scada.custom_control instead
|
|
3
3
|
"""
|
|
4
|
-
from abc import abstractmethod, ABC
|
|
5
4
|
import warnings
|
|
5
|
+
from abc import abstractmethod, ABC
|
|
6
6
|
import numpy as np
|
|
7
7
|
import epyt
|
|
8
8
|
|
|
9
9
|
from . import ScadaData
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
warnings.warn("'epyt_flow.simulation.scada.advanced_control' is deprecated and will be removed " +
|
|
13
|
+
"in future releases -- use 'epyt_flow.simulation.scada.custom_control' instead")
|
|
14
|
+
|
|
15
|
+
|
|
12
16
|
class AdvancedControlModule(ABC):
|
|
13
17
|
"""
|
|
14
18
|
Base class for a control module.
|