epyt-flow 0.1.0__tar.gz → 0.2.0__tar.gz
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-0.1.0/epyt_flow.egg-info → epyt_flow-0.2.0}/PKG-INFO +29 -5
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/README.md +26 -2
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/REQUIREMENTS.txt +1 -1
- epyt_flow-0.2.0/epyt_flow/EPANET/compile_linux.sh +4 -0
- epyt_flow-0.2.0/epyt_flow/VERSION +1 -0
- epyt_flow-0.2.0/epyt_flow/__init__.py +37 -0
- epyt_flow-0.2.0/epyt_flow/rest_api/scada_data/__init__.py +0 -0
- epyt_flow-0.1.0/epyt_flow/rest_api/scada_data_handler.py → epyt_flow-0.2.0/epyt_flow/rest_api/scada_data/data_handlers.py +3 -162
- epyt_flow-0.2.0/epyt_flow/rest_api/scada_data/export_handlers.py +140 -0
- epyt_flow-0.2.0/epyt_flow/rest_api/scada_data/handlers.py +167 -0
- epyt_flow-0.2.0/epyt_flow/rest_api/scenario/__init__.py +0 -0
- epyt_flow-0.2.0/epyt_flow/rest_api/scenario/event_handlers.py +118 -0
- epyt_flow-0.1.0/epyt_flow/rest_api/scenario_handler.py → epyt_flow-0.2.0/epyt_flow/rest_api/scenario/handlers.py +86 -67
- epyt_flow-0.2.0/epyt_flow/rest_api/scenario/simulation_handlers.py +174 -0
- epyt_flow-0.2.0/epyt_flow/rest_api/scenario/uncertainty_handlers.py +118 -0
- epyt_flow-0.2.0/epyt_flow/rest_api/server.py +141 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/scada/scada_data.py +2 -2
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/scada/scada_data_export.py +1 -7
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/scenario_config.py +12 -18
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/scenario_simulator.py +387 -132
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/sensor_config.py +370 -9
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/topology.py +47 -6
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/utils.py +75 -18
- {epyt_flow-0.1.0 → epyt_flow-0.2.0/epyt_flow.egg-info}/PKG-INFO +29 -5
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow.egg-info/SOURCES.txt +10 -3
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow.egg-info/requires.txt +1 -1
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/pyproject.toml +1 -1
- epyt_flow-0.1.0/epyt_flow/EPANET/compile.sh +0 -4
- epyt_flow-0.1.0/epyt_flow/VERSION +0 -1
- epyt_flow-0.1.0/epyt_flow/__init__.py +0 -24
- epyt_flow-0.1.0/epyt_flow/rest_api/server.py +0 -106
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/CODE_OF_CONDUCT.md +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/LICENSE +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/MANIFEST.in +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/LICENSE +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/Readme_SRC_Engines.txt +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/enumstxt.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet2.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet2.def +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/errors.dat +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/funcs.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/genmmd.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/hash.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/hash.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydcoeffs.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydraul.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydsolver.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydstatus.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_2.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_enums.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/inpfile.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/input1.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/input2.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/input3.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/main.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/mempool.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/mempool.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/output.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/project.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/quality.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/qualreact.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/qualroute.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/report.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/rules.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/smatrix.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/text.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET/SRC_engines/types.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/MSX_Updates.txt +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/dispersion.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/hash.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/hash.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx_export.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/mempool.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/mempool.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxchem.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxcompiler.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxdict.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxdispersion.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxerr.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfile.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxinp.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxout.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxproj.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxqual.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxrpt.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtank.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtoolkit.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtypes.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxutils.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxutils.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/newton.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/newton.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/rk5.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/rk5.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/ros2.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/ros2.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/smatrix.c +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/Src/smatrix.h +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/EPANET/EPANET-MSX/readme.txt +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/data/__init__.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/data/benchmarks/__init__.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/data/benchmarks/batadal.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/data/benchmarks/batadal_data.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/data/benchmarks/battledim.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/data/benchmarks/battledim_data.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/data/benchmarks/gecco_water_quality.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/data/benchmarks/leakdb.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/data/benchmarks/leakdb_data.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/data/benchmarks/water_usage.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/data/networks.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/gym/__init__.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/gym/control_gyms.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/gym/scenario_control_env.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/metrics.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/models/__init__.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/models/event_detector.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/models/sensor_interpolation_detector.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/rest_api/__init__.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/rest_api/base_handler.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/rest_api/res_manager.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/serialization.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/__init__.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/events/__init__.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/events/actuator_events.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/events/event.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/events/leakages.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/events/sensor_faults.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/events/sensor_reading_attack.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/events/sensor_reading_event.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/events/system_event.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/parallel_simulation.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/scada/__init__.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/scada/advanced_control.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/simulation/scenario_visualizer.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/uncertainty/__init__.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/uncertainty/model_uncertainty.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/uncertainty/sensor_noise.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/uncertainty/uncertainties.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow/uncertainty/utils.py +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow.egg-info/dependency_links.txt +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/epyt_flow.egg-info/top_level.txt +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/setup.cfg +0 -0
- {epyt_flow-0.1.0 → epyt_flow-0.2.0}/setup.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: epyt-flow
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: EPyT-Flow -- EPANET Python Toolkit - Flow
|
|
5
5
|
Author-email: André Artelt <aartelt@techfak.uni-bielefeld.de>, "Marios S. Kyriakou" <kiriakou.marios@ucy.ac.cy>, "Stelios G. Vrachimis" <vrachimis.stelios@ucy.ac.cy>
|
|
6
6
|
License: MIT License
|
|
7
7
|
Project-URL: Homepage, https://github.com/WaterFutures/EPyT-Flow
|
|
8
|
-
Project-URL: Documentation, https://
|
|
8
|
+
Project-URL: Documentation, https://epyt-flow.readthedocs.io/en/latest/
|
|
9
9
|
Project-URL: Repository, https://github.com/WaterFutures/EPyT-Flow.git
|
|
10
10
|
Project-URL: Issues, https://github.com/WaterFutures/EPyT-Flow/issues
|
|
11
11
|
Keywords: epanet,water,networks,hydraulics,quality,simulations
|
|
@@ -20,7 +20,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
20
20
|
Requires-Python: >=3.9
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
|
-
Requires-Dist: epyt>=1.1.
|
|
23
|
+
Requires-Dist: epyt>=1.1.6
|
|
24
24
|
Requires-Dist: requests>=2.31.0
|
|
25
25
|
Requires-Dist: scipy>=1.11.4
|
|
26
26
|
Requires-Dist: u-msgpack-python>=2.8.0
|
|
@@ -32,6 +32,13 @@ Requires-Dist: falcon>=3.1.3
|
|
|
32
32
|
Requires-Dist: multiprocess>=0.70.16
|
|
33
33
|
Requires-Dist: psutil
|
|
34
34
|
|
|
35
|
+
[](https://opensource.org/licenses/MIT)
|
|
36
|
+
[](https://pypi.org/project/epyt-flow/)
|
|
37
|
+
[](https://github.com/WaterFutures/EPyT-Flow/actions/workflows/build_tests.yml)
|
|
38
|
+
[](https://epyt-flow.readthedocs.io/en/stable/?badge=stable)
|
|
39
|
+
[](https://pepy.tech/project/epyt-flow)
|
|
40
|
+
[](https://pepy.tech/project/epyt-flow)
|
|
41
|
+
|
|
35
42
|
# EPyT-Flow -- EPANET Python Toolkit - Flow
|
|
36
43
|
|
|
37
44
|
EPyT-Flow is a Python package building on top of [EPyT](https://github.com/OpenWaterAnalytics/EPyT)
|
|
@@ -45,11 +52,28 @@ Furthermore, it also provides an environment for developing and testing control
|
|
|
45
52
|
|
|
46
53
|

|
|
47
54
|
|
|
55
|
+
|
|
56
|
+
## Unique Features
|
|
57
|
+
|
|
58
|
+
Unique features of EPyT-Flow that make it superior to other (Python) toolboxes are the following:
|
|
59
|
+
|
|
60
|
+
- High-performance hydraulic and (advanced) water quality simulation
|
|
61
|
+
- High- and low-level interface
|
|
62
|
+
- Object-orientated design that is easy to extend and customize
|
|
63
|
+
- Sensor configurations
|
|
64
|
+
- Wide variety of pre-defined events (e.g. leakages, sensor faults, actuator events, cyber-attacks, etc.)
|
|
65
|
+
- Wide variety of pre-defined types of uncertainties (e.g. model uncertainties)
|
|
66
|
+
- Step-wise simulation and environment for training and evaluating control strategies
|
|
67
|
+
- Serialization module for easy exchange of data and (scenario) configurations
|
|
68
|
+
- REST API to make EPyT-Flow accessible in other applications
|
|
69
|
+
- Access to many WDNs and popular benchmarks (incl. their evaluation)
|
|
70
|
+
|
|
71
|
+
|
|
48
72
|
## Installation
|
|
49
73
|
|
|
50
74
|
EPyT-Flow supports Python 3.9 - 3.12
|
|
51
75
|
|
|
52
|
-
Note that [EPANET and EPANET-MSX sources](epyt_flow/EPANET/) are compiled and
|
|
76
|
+
Note that [EPANET and EPANET-MSX sources](epyt_flow/EPANET/) are compiled and overwrite the binaries
|
|
53
77
|
shipped by EPyT IF EPyT-Flow is installed on a Linux system. By this we not only aim to achieve
|
|
54
78
|
a better performance of the simulations but also avoid any compatibility problems of pre-compiled binaries.
|
|
55
79
|
|
|
@@ -113,7 +137,7 @@ if __name__ == "__main__":
|
|
|
113
137
|
|
|
114
138
|
## Documentation
|
|
115
139
|
|
|
116
|
-
Documentation is available on readthedocs:
|
|
140
|
+
Documentation is available on readthedocs:[https://epyt-flow.readthedocs.io/en/latest/](https://epyt-flow.readthedocs.io/en/stable)
|
|
117
141
|
|
|
118
142
|
## License
|
|
119
143
|
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
[](https://opensource.org/licenses/MIT)
|
|
2
|
+
[](https://pypi.org/project/epyt-flow/)
|
|
3
|
+
[](https://github.com/WaterFutures/EPyT-Flow/actions/workflows/build_tests.yml)
|
|
4
|
+
[](https://epyt-flow.readthedocs.io/en/stable/?badge=stable)
|
|
5
|
+
[](https://pepy.tech/project/epyt-flow)
|
|
6
|
+
[](https://pepy.tech/project/epyt-flow)
|
|
7
|
+
|
|
1
8
|
# EPyT-Flow -- EPANET Python Toolkit - Flow
|
|
2
9
|
|
|
3
10
|
EPyT-Flow is a Python package building on top of [EPyT](https://github.com/OpenWaterAnalytics/EPyT)
|
|
@@ -11,11 +18,28 @@ Furthermore, it also provides an environment for developing and testing control
|
|
|
11
18
|
|
|
12
19
|

|
|
13
20
|
|
|
21
|
+
|
|
22
|
+
## Unique Features
|
|
23
|
+
|
|
24
|
+
Unique features of EPyT-Flow that make it superior to other (Python) toolboxes are the following:
|
|
25
|
+
|
|
26
|
+
- High-performance hydraulic and (advanced) water quality simulation
|
|
27
|
+
- High- and low-level interface
|
|
28
|
+
- Object-orientated design that is easy to extend and customize
|
|
29
|
+
- Sensor configurations
|
|
30
|
+
- Wide variety of pre-defined events (e.g. leakages, sensor faults, actuator events, cyber-attacks, etc.)
|
|
31
|
+
- Wide variety of pre-defined types of uncertainties (e.g. model uncertainties)
|
|
32
|
+
- Step-wise simulation and environment for training and evaluating control strategies
|
|
33
|
+
- Serialization module for easy exchange of data and (scenario) configurations
|
|
34
|
+
- REST API to make EPyT-Flow accessible in other applications
|
|
35
|
+
- Access to many WDNs and popular benchmarks (incl. their evaluation)
|
|
36
|
+
|
|
37
|
+
|
|
14
38
|
## Installation
|
|
15
39
|
|
|
16
40
|
EPyT-Flow supports Python 3.9 - 3.12
|
|
17
41
|
|
|
18
|
-
Note that [EPANET and EPANET-MSX sources](epyt_flow/EPANET/) are compiled and
|
|
42
|
+
Note that [EPANET and EPANET-MSX sources](epyt_flow/EPANET/) are compiled and overwrite the binaries
|
|
19
43
|
shipped by EPyT IF EPyT-Flow is installed on a Linux system. By this we not only aim to achieve
|
|
20
44
|
a better performance of the simulations but also avoid any compatibility problems of pre-compiled binaries.
|
|
21
45
|
|
|
@@ -79,7 +103,7 @@ if __name__ == "__main__":
|
|
|
79
103
|
|
|
80
104
|
## Documentation
|
|
81
105
|
|
|
82
|
-
Documentation is available on readthedocs:
|
|
106
|
+
Documentation is available on readthedocs:[https://epyt-flow.readthedocs.io/en/latest/](https://epyt-flow.readthedocs.io/en/stable)
|
|
83
107
|
|
|
84
108
|
## License
|
|
85
109
|
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
mkdir -p "../customlibs/"
|
|
3
|
+
gcc -w -O3 -march=native -shared -Wl,-soname,libepanet2_2.so -fPIC -o "../customlibs/libepanet2_2.so" EPANET/SRC_engines/*.c -IEPANET/SRC_engines/include -lc -lm -pthread
|
|
4
|
+
gcc -w -O3 -march=native -fPIC -shared -Wl,-soname,libepanetmsx2_2_0.so -o "../customlibs/libepanetmsx2_2_0.so" -fopenmp -Depanetmsx_EXPORTS -IEPANET-MSX/Src/include -IEPANET/SRC_engines/include EPANET-MSX/Src/*.c -Wl,-rpath=. "../customlibs/libepanet2_2.so" -lm -lgomp -lpthread
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.2.0
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import warnings
|
|
3
|
+
import shutil
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
with open(os.path.join(os.path.dirname(__file__), 'VERSION'), encoding="utf-8") as f:
|
|
8
|
+
VERSION = f.read().strip()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def compile_libraries_unix(lib_epanet_name, compile_script_name):
|
|
12
|
+
"""Compile EPANET and EPANET-MSX libraries if needed."""
|
|
13
|
+
path_to_custom_libs = os.path.join(os.path.dirname(__file__), "customlibs")
|
|
14
|
+
path_to_lib_epanet = os.path.join(path_to_custom_libs, lib_epanet_name)
|
|
15
|
+
path_to_epanet = os.path.join(os.path.dirname(__file__), "EPANET")
|
|
16
|
+
|
|
17
|
+
update = False
|
|
18
|
+
if os.path.isfile(path_to_lib_epanet):
|
|
19
|
+
if os.path.getmtime(__file__) > os.path.getmtime(path_to_lib_epanet):
|
|
20
|
+
update = True
|
|
21
|
+
|
|
22
|
+
if not os.path.isfile(path_to_lib_epanet) or update:
|
|
23
|
+
if shutil.which("gcc") is not None:
|
|
24
|
+
print("Compiling EPANET and EPANET-MSX...")
|
|
25
|
+
try:
|
|
26
|
+
subprocess.check_call(f"cd \"{path_to_epanet}\"; bash {compile_script_name}",
|
|
27
|
+
shell=True)
|
|
28
|
+
print("Done")
|
|
29
|
+
except subprocess.CalledProcessError as ex:
|
|
30
|
+
print(f"Compilation failed\n{ex}")
|
|
31
|
+
else:
|
|
32
|
+
warnings.warn("GCC is not available to compile the required libraries.\n" +
|
|
33
|
+
"Falling back to pre-compiled library shipped by EPyT.")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
if sys.platform.startswith("linux"):
|
|
37
|
+
compile_libraries_unix("libepanet2_2.so", "compile_linux.sh")
|
|
File without changes
|
|
@@ -1,170 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
This module provides REST API handlers for accessing the final sensor readings
|
|
3
|
+
(e.g. pressure, flow rate, etc.).
|
|
3
4
|
"""
|
|
4
5
|
import warnings
|
|
5
6
|
import falcon
|
|
6
7
|
|
|
7
|
-
from .
|
|
8
|
-
from .res_manager import ResourceManager
|
|
9
|
-
from ..simulation import SensorConfig, SensorFault
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class ScadaDataManager(ResourceManager):
|
|
13
|
-
"""
|
|
14
|
-
Class for managing SCADA data.
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class ScadaDataBaseHandler(BaseHandler):
|
|
19
|
-
"""
|
|
20
|
-
Base class for all handlers concerning SCADA data.
|
|
21
|
-
|
|
22
|
-
Parameters
|
|
23
|
-
----------
|
|
24
|
-
scada_data_mgr : `~epyt_flow.rest_api.scenario_handler.ScadaDataBaseHandler`
|
|
25
|
-
SCADA data manager.
|
|
26
|
-
"""
|
|
27
|
-
def __init__(self, scada_data_mgr: ScadaDataManager):
|
|
28
|
-
self.scada_data_mgr = scada_data_mgr
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class ScadaDataRemoveHandler(ScadaDataBaseHandler):
|
|
32
|
-
"""
|
|
33
|
-
Class for handling a DELETE request for a given SCADA data instance.
|
|
34
|
-
"""
|
|
35
|
-
def on_delete(self, _, resp: falcon.Response, data_id: str) -> None:
|
|
36
|
-
"""
|
|
37
|
-
Deletes a given SCADA data instance.
|
|
38
|
-
|
|
39
|
-
Parameters
|
|
40
|
-
----------
|
|
41
|
-
resp : `falcon.Response`
|
|
42
|
-
Response instance.
|
|
43
|
-
data_id : `str`
|
|
44
|
-
UUID of the SCADA data instance.
|
|
45
|
-
"""
|
|
46
|
-
try:
|
|
47
|
-
if self.scada_data_mgr.validate_uuid(data_id) is False:
|
|
48
|
-
self.send_invalid_resource_id_error(resp)
|
|
49
|
-
return
|
|
50
|
-
|
|
51
|
-
self.scada_data_mgr.remove(data_id)
|
|
52
|
-
except Exception as ex:
|
|
53
|
-
warnings.warn(str(ex))
|
|
54
|
-
resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
class ScadaDataSensorConfigHandler(ScadaDataBaseHandler):
|
|
58
|
-
"""
|
|
59
|
-
Class for handling GET and POST requests for the sensor configuration
|
|
60
|
-
of a given SCADA data instance.
|
|
61
|
-
"""
|
|
62
|
-
def on_get(self, _, resp: falcon.Response, data_id: str) -> None:
|
|
63
|
-
"""
|
|
64
|
-
Gets the sensor configuration of a given SCADA data instance.
|
|
65
|
-
|
|
66
|
-
Parameters
|
|
67
|
-
----------
|
|
68
|
-
resp : `falcon.Response`
|
|
69
|
-
Response instance.
|
|
70
|
-
data_id : `str`
|
|
71
|
-
UUID of the SCADA data.
|
|
72
|
-
"""
|
|
73
|
-
try:
|
|
74
|
-
if self.scada_data_mgr.validate_uuid(data_id) is False:
|
|
75
|
-
self.send_invalid_resource_id_error(resp)
|
|
76
|
-
return
|
|
77
|
-
|
|
78
|
-
my_sensor_config = self.scada_data_mgr.get(data_id).sensor_config
|
|
79
|
-
self.send_json_response(resp, my_sensor_config)
|
|
80
|
-
except Exception as ex:
|
|
81
|
-
warnings.warn(str(ex))
|
|
82
|
-
resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
|
|
83
|
-
|
|
84
|
-
def on_post(self, req: falcon.Request, resp: falcon.Response, data_id: str) -> None:
|
|
85
|
-
"""
|
|
86
|
-
Sets the sensor configuration of a given SCADA data instance.
|
|
87
|
-
|
|
88
|
-
Parameters
|
|
89
|
-
----------
|
|
90
|
-
req : `falcon.Request`
|
|
91
|
-
Request instance.
|
|
92
|
-
resp : `falcon.Response`
|
|
93
|
-
Response instance.
|
|
94
|
-
data_id : `str`
|
|
95
|
-
UUID of the SCADA data.
|
|
96
|
-
"""
|
|
97
|
-
try:
|
|
98
|
-
if self.scada_data_mgr.validate_uuid(data_id) is False:
|
|
99
|
-
self.send_invalid_resource_id_error(resp)
|
|
100
|
-
return
|
|
101
|
-
|
|
102
|
-
sensor_config = self.load_json_data_from_request(req)
|
|
103
|
-
if not isinstance(sensor_config, SensorConfig):
|
|
104
|
-
self.send_json_parsing_error(resp)
|
|
105
|
-
return
|
|
106
|
-
|
|
107
|
-
self.scada_data_mgr.get(data_id).sensor_config = sensor_config
|
|
108
|
-
except Exception as ex:
|
|
109
|
-
warnings.warn(str(ex))
|
|
110
|
-
resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
class ScadaDataSensorFaultsHandler(ScadaDataBaseHandler):
|
|
114
|
-
"""
|
|
115
|
-
Class for handling GET and POST requests concerning sensor faults in a
|
|
116
|
-
given SCADA data instance.
|
|
117
|
-
"""
|
|
118
|
-
def on_get(self, _, resp: falcon.Response, data_id: str) -> None:
|
|
119
|
-
"""
|
|
120
|
-
Gets all sensor faults of a given SCADA data instance.
|
|
121
|
-
|
|
122
|
-
Parameters
|
|
123
|
-
----------
|
|
124
|
-
resp : `falcon.Response`
|
|
125
|
-
Response instance.
|
|
126
|
-
data_id : `str`
|
|
127
|
-
UUID of the SCADA data.
|
|
128
|
-
"""
|
|
129
|
-
try:
|
|
130
|
-
if self.scada_data_mgr.validate_uuid(data_id) is False:
|
|
131
|
-
self.send_invalid_resource_id_error(resp)
|
|
132
|
-
return
|
|
133
|
-
|
|
134
|
-
sensor_faults = self.scada_data_mgr.get(data_id).sensor_faults
|
|
135
|
-
self.send_json_response(resp, sensor_faults)
|
|
136
|
-
except Exception as ex:
|
|
137
|
-
warnings.warn(str(ex))
|
|
138
|
-
resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
|
|
139
|
-
|
|
140
|
-
def on_post(self, req: falcon.Request, resp: falcon.Response, data_id: str) -> None:
|
|
141
|
-
"""
|
|
142
|
-
Sets (i.e. overrides) the sensor faults in a given SCADA data instance.
|
|
143
|
-
|
|
144
|
-
Parameters
|
|
145
|
-
----------
|
|
146
|
-
req : `falcon.Request`
|
|
147
|
-
Request instance.
|
|
148
|
-
resp : `falcon.Response`
|
|
149
|
-
Response instance.
|
|
150
|
-
data_id : `str`
|
|
151
|
-
UUID of the SCADA data.
|
|
152
|
-
"""
|
|
153
|
-
try:
|
|
154
|
-
if self.scada_data_mgr.validate_uuid(data_id) is False:
|
|
155
|
-
self.send_invalid_resource_id_error(resp)
|
|
156
|
-
return
|
|
157
|
-
|
|
158
|
-
sensor_faults = self.load_json_data_from_request(req)
|
|
159
|
-
if not isinstance(sensor_faults, list) or \
|
|
160
|
-
any(not isinstance(e, SensorFault) for e in sensor_faults):
|
|
161
|
-
self.send_json_parsing_error(resp)
|
|
162
|
-
return
|
|
163
|
-
|
|
164
|
-
self.scada_data_mgr.get(data_id).sensor_faults = sensor_faults
|
|
165
|
-
except Exception as ex:
|
|
166
|
-
warnings.warn(str(ex))
|
|
167
|
-
resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
|
|
8
|
+
from .handlers import ScadaDataBaseHandler
|
|
168
9
|
|
|
169
10
|
|
|
170
11
|
class ScadaDataPressuresHandler(ScadaDataBaseHandler):
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides REST API handlers for exporting a given SCADA data instance.
|
|
3
|
+
"""
|
|
4
|
+
import os
|
|
5
|
+
from abc import abstractmethod
|
|
6
|
+
import warnings
|
|
7
|
+
import falcon
|
|
8
|
+
|
|
9
|
+
from .handlers import ScadaDataBaseHandler
|
|
10
|
+
from ...utils import get_temp_folder
|
|
11
|
+
from ...simulation.scada import ScadaData, ScadaDataNumpyExport, ScadaDataMatlabExport, \
|
|
12
|
+
ScadaDataXlsxExport
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ScadaDataBaseExportHandler(ScadaDataBaseHandler):
|
|
16
|
+
"""
|
|
17
|
+
Base handler for exporting a given SCADA data instance.
|
|
18
|
+
"""
|
|
19
|
+
def __init__(self, file_ext: str, **kwds):
|
|
20
|
+
self.__file_ext = file_ext
|
|
21
|
+
|
|
22
|
+
super().__init__(**kwds)
|
|
23
|
+
|
|
24
|
+
def create_temp_file_path(self, data_id: str, file_ext: str) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Returns a path to a temporary file for storing the SCADA data instance.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
data_id : `str`
|
|
31
|
+
UUID of the SCADA data.
|
|
32
|
+
file_ext : `str`
|
|
33
|
+
File extension.
|
|
34
|
+
"""
|
|
35
|
+
return os.path.join(get_temp_folder(), f"{data_id}.{file_ext}")
|
|
36
|
+
|
|
37
|
+
def send_temp_file(self, resp: falcon.Response, tmp_file: str,
|
|
38
|
+
content_type: str = "application/octet-stream") -> None:
|
|
39
|
+
"""
|
|
40
|
+
Sends a given file (`tmp_file`) to the the client.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
resp : `falcon.Response`
|
|
45
|
+
Response instance.
|
|
46
|
+
tmp_file : `str`
|
|
47
|
+
Path to the temporary file to be send.
|
|
48
|
+
"""
|
|
49
|
+
resp.status = falcon.HTTP_200
|
|
50
|
+
resp.content_type = content_type
|
|
51
|
+
with open(tmp_file, 'rb') as f:
|
|
52
|
+
resp.text = f.read()
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def export(self, scada_data: ScadaData, tmp_file: str) -> None:
|
|
56
|
+
"""
|
|
57
|
+
Exports a given SCADA data instance to a temporary file.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
scada_data : :class:`~epyt_flow.simulation.scada.scada_data.ScadaData`
|
|
62
|
+
SCADA data instance to be exported.
|
|
63
|
+
tmp_file : `str`
|
|
64
|
+
Path to temporary file.
|
|
65
|
+
"""
|
|
66
|
+
raise NotImplementedError()
|
|
67
|
+
|
|
68
|
+
def on_get(self, _, resp: falcon.Response, data_id: str) -> None:
|
|
69
|
+
"""
|
|
70
|
+
Gets the given SCADA data instance.
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
resp : `falcon.Response`
|
|
75
|
+
Response instance.
|
|
76
|
+
data_id : `str`
|
|
77
|
+
UUID of the SCADA data.
|
|
78
|
+
"""
|
|
79
|
+
try:
|
|
80
|
+
if self.scada_data_mgr.validate_uuid(data_id) is False:
|
|
81
|
+
self.send_invalid_resource_id_error(resp)
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
my_scada_data = self.scada_data_mgr.get(data_id)
|
|
85
|
+
|
|
86
|
+
tmp_file = self.create_temp_file_path(data_id, self.__file_ext)
|
|
87
|
+
self.export(my_scada_data, tmp_file)
|
|
88
|
+
|
|
89
|
+
self.send_temp_file(resp, tmp_file)
|
|
90
|
+
|
|
91
|
+
os.remove(tmp_file)
|
|
92
|
+
except Exception as ex:
|
|
93
|
+
warnings.warn(str(ex))
|
|
94
|
+
resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class ScadaDataExportHandler(ScadaDataBaseExportHandler):
|
|
98
|
+
"""
|
|
99
|
+
Class for handling a GET requests for exporting a given SCADA data instance
|
|
100
|
+
to an .epytflow_scada_data file.
|
|
101
|
+
"""
|
|
102
|
+
def __init__(self, **kwds):
|
|
103
|
+
super().__init__(file_ext=".epytflow_scada_data", **kwds)
|
|
104
|
+
|
|
105
|
+
def export(self, scada_data: ScadaData, tmp_file: str) -> None:
|
|
106
|
+
scada_data.save_to_file(tmp_file)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class ScadaDataXlsxExportHandler(ScadaDataBaseExportHandler):
|
|
110
|
+
"""
|
|
111
|
+
Class for handling a GET requests for exporting a given SCADA data instance to a .xlsx file.
|
|
112
|
+
"""
|
|
113
|
+
def __init__(self, **kwds):
|
|
114
|
+
super().__init__(file_ext=".xlsx", **kwds)
|
|
115
|
+
|
|
116
|
+
def export(self, scada_data: ScadaData, tmp_file: str) -> None:
|
|
117
|
+
ScadaDataXlsxExport(tmp_file).export(scada_data)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class ScadaDataNumpyExportHandler(ScadaDataBaseExportHandler):
|
|
121
|
+
"""
|
|
122
|
+
Class for handling a GET requests for exporting a given SCADA data instance to Numpy data file.
|
|
123
|
+
"""
|
|
124
|
+
def __init__(self, **kwds):
|
|
125
|
+
super().__init__(file_ext=".npz", **kwds)
|
|
126
|
+
|
|
127
|
+
def export(self, scada_data: ScadaData, tmp_file: str) -> None:
|
|
128
|
+
ScadaDataNumpyExport(tmp_file).export(scada_data)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class ScadaDataMatlabExportHandler(ScadaDataBaseExportHandler):
|
|
132
|
+
"""
|
|
133
|
+
Class for handling a GET requests for exporting a given SCADA data instance
|
|
134
|
+
to a Matlab data file.
|
|
135
|
+
"""
|
|
136
|
+
def __init__(self, **kwds):
|
|
137
|
+
super().__init__(file_ext=".mat", **kwds)
|
|
138
|
+
|
|
139
|
+
def export(self, scada_data: ScadaData, tmp_file: str) -> None:
|
|
140
|
+
ScadaDataMatlabExport(tmp_file).export(scada_data)
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The module provides REST API handlers for some SCADA data requests.
|
|
3
|
+
"""
|
|
4
|
+
import warnings
|
|
5
|
+
import falcon
|
|
6
|
+
|
|
7
|
+
from ..base_handler import BaseHandler
|
|
8
|
+
from ..res_manager import ResourceManager
|
|
9
|
+
from ...simulation import SensorConfig, SensorFault
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ScadaDataManager(ResourceManager):
|
|
13
|
+
"""
|
|
14
|
+
Class for managing SCADA data.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ScadaDataBaseHandler(BaseHandler):
|
|
19
|
+
"""
|
|
20
|
+
Base class for all handlers concerning SCADA data.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
scada_data_mgr : :class:`~epyt_flow.rest_api.scada_data.handlers.ScadaDataManager`
|
|
25
|
+
SCADA data manager.
|
|
26
|
+
"""
|
|
27
|
+
def __init__(self, scada_data_mgr: ScadaDataManager):
|
|
28
|
+
self.scada_data_mgr = scada_data_mgr
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ScadaDataRemoveHandler(ScadaDataBaseHandler):
|
|
32
|
+
"""
|
|
33
|
+
Class for handling a DELETE request for a given SCADA data instance.
|
|
34
|
+
"""
|
|
35
|
+
def on_delete(self, _, resp: falcon.Response, data_id: str) -> None:
|
|
36
|
+
"""
|
|
37
|
+
Deletes a given SCADA data instance.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
resp : `falcon.Response`
|
|
42
|
+
Response instance.
|
|
43
|
+
data_id : `str`
|
|
44
|
+
UUID of the SCADA data instance.
|
|
45
|
+
"""
|
|
46
|
+
try:
|
|
47
|
+
if self.scada_data_mgr.validate_uuid(data_id) is False:
|
|
48
|
+
self.send_invalid_resource_id_error(resp)
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
self.scada_data_mgr.remove(data_id)
|
|
52
|
+
except Exception as ex:
|
|
53
|
+
warnings.warn(str(ex))
|
|
54
|
+
resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ScadaDataSensorConfigHandler(ScadaDataBaseHandler):
|
|
58
|
+
"""
|
|
59
|
+
Class for handling GET and POST requests for the sensor configuration
|
|
60
|
+
of a given SCADA data instance.
|
|
61
|
+
"""
|
|
62
|
+
def on_get(self, _, resp: falcon.Response, data_id: str) -> None:
|
|
63
|
+
"""
|
|
64
|
+
Gets the sensor configuration of a given SCADA data instance.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
resp : `falcon.Response`
|
|
69
|
+
Response instance.
|
|
70
|
+
data_id : `str`
|
|
71
|
+
UUID of the SCADA data.
|
|
72
|
+
"""
|
|
73
|
+
try:
|
|
74
|
+
if self.scada_data_mgr.validate_uuid(data_id) is False:
|
|
75
|
+
self.send_invalid_resource_id_error(resp)
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
my_sensor_config = self.scada_data_mgr.get(data_id).sensor_config
|
|
79
|
+
self.send_json_response(resp, my_sensor_config)
|
|
80
|
+
except Exception as ex:
|
|
81
|
+
warnings.warn(str(ex))
|
|
82
|
+
resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
|
|
83
|
+
|
|
84
|
+
def on_post(self, req: falcon.Request, resp: falcon.Response, data_id: str) -> None:
|
|
85
|
+
"""
|
|
86
|
+
Sets the sensor configuration of a given SCADA data instance.
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
req : `falcon.Request`
|
|
91
|
+
Request instance.
|
|
92
|
+
resp : `falcon.Response`
|
|
93
|
+
Response instance.
|
|
94
|
+
data_id : `str`
|
|
95
|
+
UUID of the SCADA data.
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
if self.scada_data_mgr.validate_uuid(data_id) is False:
|
|
99
|
+
self.send_invalid_resource_id_error(resp)
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
sensor_config = self.load_json_data_from_request(req)
|
|
103
|
+
if not isinstance(sensor_config, SensorConfig):
|
|
104
|
+
self.send_json_parsing_error(resp)
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
self.scada_data_mgr.get(data_id).sensor_config = sensor_config
|
|
108
|
+
except Exception as ex:
|
|
109
|
+
warnings.warn(str(ex))
|
|
110
|
+
resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class ScadaDataSensorFaultsHandler(ScadaDataBaseHandler):
|
|
114
|
+
"""
|
|
115
|
+
Class for handling GET and POST requests concerning sensor faults in a
|
|
116
|
+
given SCADA data instance.
|
|
117
|
+
"""
|
|
118
|
+
def on_get(self, _, resp: falcon.Response, data_id: str) -> None:
|
|
119
|
+
"""
|
|
120
|
+
Gets all sensor faults of a given SCADA data instance.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
resp : `falcon.Response`
|
|
125
|
+
Response instance.
|
|
126
|
+
data_id : `str`
|
|
127
|
+
UUID of the SCADA data.
|
|
128
|
+
"""
|
|
129
|
+
try:
|
|
130
|
+
if self.scada_data_mgr.validate_uuid(data_id) is False:
|
|
131
|
+
self.send_invalid_resource_id_error(resp)
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
sensor_faults = self.scada_data_mgr.get(data_id).sensor_faults
|
|
135
|
+
self.send_json_response(resp, sensor_faults)
|
|
136
|
+
except Exception as ex:
|
|
137
|
+
warnings.warn(str(ex))
|
|
138
|
+
resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
|
|
139
|
+
|
|
140
|
+
def on_post(self, req: falcon.Request, resp: falcon.Response, data_id: str) -> None:
|
|
141
|
+
"""
|
|
142
|
+
Sets (i.e. overrides) the sensor faults in a given SCADA data instance.
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
req : `falcon.Request`
|
|
147
|
+
Request instance.
|
|
148
|
+
resp : `falcon.Response`
|
|
149
|
+
Response instance.
|
|
150
|
+
data_id : `str`
|
|
151
|
+
UUID of the SCADA data.
|
|
152
|
+
"""
|
|
153
|
+
try:
|
|
154
|
+
if self.scada_data_mgr.validate_uuid(data_id) is False:
|
|
155
|
+
self.send_invalid_resource_id_error(resp)
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
sensor_faults = self.load_json_data_from_request(req)
|
|
159
|
+
if not isinstance(sensor_faults, list) or \
|
|
160
|
+
any(not isinstance(e, SensorFault) for e in sensor_faults):
|
|
161
|
+
self.send_json_parsing_error(resp)
|
|
162
|
+
return
|
|
163
|
+
|
|
164
|
+
self.scada_data_mgr.get(data_id).sensor_faults = sensor_faults
|
|
165
|
+
except Exception as ex:
|
|
166
|
+
warnings.warn(str(ex))
|
|
167
|
+
resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
|
|
File without changes
|