diff --git a/.idea/EvaluationEfficiencyOptimizationWind.iml b/.idea/EvaluationEfficiencyOptimizationWind.iml
index d0876a7..32cf6f3 100644
--- a/.idea/EvaluationEfficiencyOptimizationWind.iml
+++ b/.idea/EvaluationEfficiencyOptimizationWind.iml
@@ -1,8 +1,22 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 0000000..7b6529f
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ mariadb
+ true
+ org.mariadb.jdbc.Driver
+ jdbc:mariadb://193.124.203.110:3306/wind_towers
+ $ProjectFileDir$
+
+
+
\ No newline at end of file
diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml
new file mode 100644
index 0000000..6fa30af
--- /dev/null
+++ b/.idea/jsLibraryMappings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index d56657a..9af92d1 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,7 @@
-
+
+
+
+
\ No newline at end of file
diff --git a/.idea/other.xml b/.idea/other.xml
new file mode 100644
index 0000000..2e75c2e
--- /dev/null
+++ b/.idea/other.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/webResources.xml b/.idea/webResources.xml
new file mode 100644
index 0000000..913f4b6
--- /dev/null
+++ b/.idea/webResources.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/front/webpack.config.js b/front/webpack.config.js
index d840890..a40ac79 100644
--- a/front/webpack.config.js
+++ b/front/webpack.config.js
@@ -5,7 +5,7 @@ import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
const __dirname = path.resolve();
const config = {
- entry: './src/index.tsx',
+ entry: './routers/index.tsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js',
diff --git a/server/README.md b/server/README.md
index 5e79c49..f69b485 100644
--- a/server/README.md
+++ b/server/README.md
@@ -20,8 +20,13 @@
```zsh
pip install -r requirements.txt
```
-
-4. Start FastAPI process
+
+4. Enter src directory
```zsh
- fastapi dev main.py
+ cd src
+ ```
+
+5. Start FastAPI process
+ ```zsh
+ uvicorn main:app --reload
```
\ No newline at end of file
diff --git a/server/database.py b/server/database.py
deleted file mode 100644
index 2776e93..0000000
--- a/server/database.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from sqlalchemy import create_engine, Column, Integer, String
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import sessionmaker
-
-
-DATABASE_URL = "mysql+pymysql://wind:wind@193.124.203.110:3306/wind_towers"
-
-
-
-engine = create_engine(DATABASE_URL)
-SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
-
-
-Base = declarative_base()
-
-
-class User(Base):
- __tablename__ = "user" # Убедитесь, что эта таблица существует
-
- id = Column(Integer, primary_key=True, index=True)
- name = Column(String(50), index=True)
diff --git a/server/floris_module/.gitignore b/server/floris_module/.gitignore
new file mode 100644
index 0000000..2d62eb2
--- /dev/null
+++ b/server/floris_module/.gitignore
@@ -0,0 +1,241 @@
+### JetBrains template
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# AWS User-specific
+.idea/**/aws.xml
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# SonarLint plugin
+.idea/sonarlint/
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### Python template
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
diff --git a/server/floris_module/NOTES.md b/server/floris_module/NOTES.md
new file mode 100644
index 0000000..9a03ec4
--- /dev/null
+++ b/server/floris_module/NOTES.md
@@ -0,0 +1,46 @@
+# Заметки по Floris
+
+## Описание
+Здесь я буду описывать общую информацию для всех,
+чтобы остальным не копать глубоко как пользоваться библиотекой
+
+
+## Инициализация и концепты библиотеки
+
+1) Для работы Floris **требуется** специальный конфигурационный yaml файл.
+В нем хранятся базовые данные о ветряках - их расположения, сила ветра и тому подобное.
+Базовое его содержимое не так важно, так как впоследствии их можно будет изменить напрямую в процессе работы программы
+(Буду постепенно искать интересующие нас параметры)
+
+2) Для инициализации используется класс `FlorisModel(yaml_path)` в библиотеке Floris. Объявляем переменную этим классом,
+и передаем в него путь к нашему Yaml файлу
+[Пример](https://nrel.github.io/floris/intro_concepts.html#build-the-model)
+
+3) Для изменения параметров заданных в yaml файле, используется метод set
+[Пример](https://nrel.github.io/floris/intro_concepts.html#run-the-floris-wake-calculation)
+
+4) Запуск симуляции происходит с помощью ```fmodel.run()```
+[Пример](https://nrel.github.io/floris/intro_concepts.html#run-the-floris-wake-calculation)
+
+5) Получаем мощность турбин с помощью ```fmodel.get_turbine_powers()``` в ваттах в виде массива Numpy.
+[Пример](https://nrel.github.io/floris/intro_concepts.html#get-turbine-power)
+
+6) Для каждого ветряка можно задать Yaw угол (Что такое Yaw угол показано на картинке)
+[Пример](https://nrel.github.io/floris/intro_concepts.html#applying-yaw-angles)
+
+![Yaw_example.png](docs/Yaw_example.png)
+
+
+
+
+## Параметры
+1) farm - Общие настройки расположения турбин (Кол-во элементов, задает кол-во ветряков. Можно поставить 1 элемент,
+в таком случае данные у всех ветряков будут одинаковые)
+ 1) layout_x - Список float координат турбин по оси X
+ 2) layout_y - Список float координат турбин по оси Y
+ 3) turbine_type - Список типов турбин
+2) flow_field - Общие настройки атмосферных параметров (Кол-во элементов, задает кол-во экспериментов)
+ 1) wind_directions - Список float Направление ветра в градусах (при этом север принимается за 0° или 360°,
+ восток – за 90°, юг – за 180°, а запад – за 270°)
+ 2) turbulence_intensities - Список float силы? ветряков
+ 3) wind_speeds - Cписок скорости ветра (Наверное в м/с)
\ No newline at end of file
diff --git a/server/floris_module/docs/Yaw_example.png b/server/floris_module/docs/Yaw_example.png
new file mode 100644
index 0000000..7b4d7b5
Binary files /dev/null and b/server/floris_module/docs/Yaw_example.png differ
diff --git a/server/floris_module/requirements.txt b/server/floris_module/requirements.txt
new file mode 100644
index 0000000..6a93f4f
--- /dev/null
+++ b/server/floris_module/requirements.txt
@@ -0,0 +1,22 @@
+attrs==24.2.0
+coloredlogs==15.0.1
+contourpy==1.3.0
+cycler==0.12.1
+FLORIS==4.1.1
+fonttools==4.54.1
+humanfriendly==10.0
+kiwisolver==1.4.7
+matplotlib==3.9.2
+numexpr==2.10.1
+numpy==1.26.4
+packaging==24.1
+pandas==2.2.3
+pillow==10.4.0
+pyparsing==3.2.0
+python-dateutil==2.9.0.post0
+pytz==2024.2
+PyYAML==6.0.2
+scipy==1.14.1
+shapely==2.0.6
+six==1.16.0
+tzdata==2024.2
diff --git a/server/floris_module/src/FlorisULSTU.py b/server/floris_module/src/FlorisULSTU.py
new file mode 100644
index 0000000..d0971ac
--- /dev/null
+++ b/server/floris_module/src/FlorisULSTU.py
@@ -0,0 +1,74 @@
+import os.path
+import sys
+import uuid
+from pathlib import Path
+
+from floris import FlorisModel
+from floris.flow_visualization import visualize_cut_plane
+from floris.layout_visualization import plot_turbine_labels
+import floris.layout_visualization as layoutviz
+
+import matplotlib.pyplot as plt
+
+
+_wind_direction_to_val: dict[str, float] = {
+ "N": 0.0,
+ "E": 90.0,
+ "S": 180.0,
+ "W": 270.0,
+}
+
+
+def _convert_winds_list_direction_definitions(
+ wind_directions: list[str],
+) -> list[float]:
+ return list(map(lambda x: _convert_wind_direction_definition(x.upper()), wind_directions))
+
+
+def _convert_wind_direction_definition(
+ wind_direction: str
+) -> float:
+ if not _check_wind_direction_definition(wind_direction):
+ raise ValueError(f"Wind direction {wind_direction} is not valid")
+
+ return sum(map(lambda x: _wind_direction_to_val[x], wind_direction)) / len(wind_direction)
+
+
+def _check_wind_direction_definition(wind_direction: str) -> bool:
+ if len(set(wind_direction) | set(_wind_direction_to_val)) > len(_wind_direction_to_val):
+ return False
+ return True
+
+
+class FlorisULSTU(FlorisModel):
+ def __init__(
+ self,
+ url_yaml: Path = Path(__file__).parent / "gch.yaml"
+ ):
+ super().__init__(url_yaml)
+
+ def set(self, **kwargs):
+ if ("wind_directions" in kwargs) and (type(kwargs["wind_directions"][0]) is str):
+ kwargs["wind_directions"] = _convert_winds_list_direction_definitions(kwargs["wind_directions"])
+
+ super().set(**kwargs)
+
+ def visualisation(self):
+
+
+ self.reset_operation()
+
+ horizontal_plane = self.calculate_horizontal_plane(height=90.0)
+ visualize_cut_plane(
+ horizontal_plane,
+ )
+
+ # plot_turbine_labels(self, axarr[0, 0])
+ filename = str(uuid.uuid4()) + ".png"
+ plt.savefig(Path(__file__).parent.parent.parent / f"public/floris/{filename}")
+ plt.close()
+
+ return filename
+
+
+
diff --git a/server/floris_module/src/__init__.py b/server/floris_module/src/__init__.py
new file mode 100644
index 0000000..4cc26c6
--- /dev/null
+++ b/server/floris_module/src/__init__.py
@@ -0,0 +1,3 @@
+from .FlorisULSTU import FlorisULSTU
+
+__all__ = ['FlorisULSTU']
\ No newline at end of file
diff --git a/server/floris_module/src/gch.yaml b/server/floris_module/src/gch.yaml
new file mode 100644
index 0000000..01d727d
--- /dev/null
+++ b/server/floris_module/src/gch.yaml
@@ -0,0 +1,244 @@
+
+###
+# A name for this input file.
+# This is not currently only for the user's reference.
+name: GCH
+
+###
+# A description of the contents of this input file.
+# This is not currently only for the user's reference.
+description: Three turbines using Gauss Curl Hybrid model
+
+###
+# The earliest verion of FLORIS this input file supports.
+# This is not currently only for the user's reference.
+floris_version: v4
+
+###
+# Configure the logging level and where to show the logs.
+logging:
+
+ ###
+ # Settings for logging to the console (i.e. terminal).
+ console:
+
+ ###
+ # Can be "true" or "false".
+ enable: true
+
+ ###
+ # Set the severity to show output. Messages at this level or higher will be shown.
+ # Can be one of "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG".
+ level: WARNING
+
+ ###
+ # Settings for logging to a file.
+ file:
+
+ ###
+ # Can be "true" or "false".
+ enable: false
+
+ ###
+ # Set the severity to show output. Messages at this level or higher will be shown.
+ # Can be one of "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG".
+ level: WARNING
+
+###
+# Configure the solver for the type of simulation.
+solver:
+
+ ###
+ # Select the solver type.
+ # Can be one of: "turbine_grid", "flow_field_grid", "flow_field_planar_grid".
+ type: turbine_grid
+
+ ###
+ # Options for the turbine type selected above. See the solver documentation for available parameters.
+ turbine_grid_points: 3
+
+###
+# Configure the turbine types and their placement within the wind farm.
+farm:
+
+ ###
+ # Coordinates for the turbine locations in the x-direction which is typically considered
+ # to be the streamwise direction (left, right) when the wind is out of the west.
+ # The order of the coordinates here corresponds to the index of the turbine in the primary
+ # data structures.
+ layout_x:
+ - 0.0
+ - 630.0
+ - 1260.0
+
+ ###
+ # Coordinates for the turbine locations in the y-direction which is typically considered
+ # to be the spanwise direction (up, down) when the wind is out of the west.
+ # The order of the coordinates here corresponds to the index of the turbine in the primary
+ # data structures.
+ layout_y:
+ - 0.0
+ - 0.0
+ - 0.0
+
+ ###
+ # Listing of turbine types for placement at the x and y coordinates given above.
+ # The list length must be 1 or the same as ``layout_x`` and ``layout_y``. If it is a
+ # single value, all turbines are of the same type. Otherwise, the turbine type
+ # is mapped to the location at the same index in ``layout_x`` and ``layout_y``.
+ # The types can be either a name included in the turbine_library or
+ # a full definition of a wind turbine directly.
+ turbine_type:
+ - nrel_5MW
+
+###
+# Configure the atmospheric conditions.
+flow_field:
+
+ ###
+ # Air density.
+ air_density: 1.225
+
+ ###
+ # The height to consider the "center" of the vertical wind speed profile
+ # due to shear. With a shear exponent not 1, the wind speed at this height
+ # will be the value given in ``wind_speeds``. Above and below this height,
+ # the wind speed will change according to the shear profile; see
+ # :py:meth:`.FlowField.initialize_velocity_field`.
+ # For farms consisting of one wind turbine type, use ``reference_wind_height: -1``
+ # to use the hub height of the wind turbine definition. For multiple wind turbine
+ # types, the reference wind height must be given explicitly.
+ reference_wind_height: -1
+
+ ###
+ # The turbulence intensities to include in the simulation, specified as a decimal.
+ turbulence_intensities:
+ - 0.06
+
+ ###
+ # The wind directions to include in the simulation.
+ # 0 is north and 270 is west.
+ wind_directions:
+ - 270.0
+
+ ###
+ # The exponent used to model the wind shear profile; see
+ # :py:meth:`.FlowField.initialize_velocity_field`.
+ wind_shear: 0.12
+
+ ###
+ # The wind speeds to include in the simulation.
+ wind_speeds:
+ - 8.0
+
+ ###
+ # The wind veer as a constant value for all points in the grid.
+ wind_veer: 0.0
+
+ ###
+ # The conditions that are specified for use with the multi-dimensional Cp/Ct capbility.
+ # These conditions are external to FLORIS and specified by the user. They are used internally
+ # through a nearest-neighbor selection process to choose the correct Cp/Ct interpolants
+ # to use.
+ multidim_conditions:
+ Tp: 2.5
+ Hs: 3.01
+
+###
+# Configure the wake model.
+wake:
+
+ ###
+ # Select the data to use for the simulation.
+ # See :py:mod:`~.wake` for a list
+ # of available data and their descriptions.
+ model_strings:
+
+ ###
+ # Select the wake combination model.
+ combination_model: sosfs
+
+ ###
+ # Select the wake deflection model.
+ deflection_model: gauss
+
+ ###
+ # Select the wake turbulence model.
+ turbulence_model: crespo_hernandez
+
+ ###
+ # Select the wake velocity deficit model.
+ velocity_model: gauss
+
+ ###
+ # Can be "true" or "false".
+ enable_secondary_steering: true
+
+ ###
+ # Can be "true" or "false".
+ enable_yaw_added_recovery: true
+
+ ###
+ # Can be "true" or "false".
+ enable_active_wake_mixing: false
+
+ ###
+ # Can be "true" or "false".
+ enable_transverse_velocities: true
+
+ ###
+ # Configure the parameters for the wake deflection model
+ # selected above.
+ # Additional blocks can be provided for
+ # data that are not enabled, but the enabled model
+ # must have a corresponding parameter block.
+ wake_deflection_parameters:
+ gauss:
+ ad: 0.0
+ alpha: 0.58
+ bd: 0.0
+ beta: 0.077
+ dm: 1.0
+ ka: 0.38
+ kb: 0.004
+ jimenez:
+ ad: 0.0
+ bd: 0.0
+ kd: 0.05
+
+ ###
+ # Configure the parameters for the wake velocity deficit model
+ # selected above.
+ # Additional blocks can be provided for
+ # data that are not enabled, but the enabled model
+ # must have a corresponding parameter block.
+ wake_velocity_parameters:
+ cc:
+ a_s: 0.179367259
+ b_s: 0.0118889215
+ c_s1: 0.0563691592
+ c_s2: 0.13290157
+ a_f: 3.11
+ b_f: -0.68
+ c_f: 2.41
+ alpha_mod: 1.0
+ gauss:
+ alpha: 0.58
+ beta: 0.077
+ ka: 0.38
+ kb: 0.004
+ jensen:
+ we: 0.05
+
+ ###
+ # Configure the parameters for the wake turbulence model
+ # selected above.
+ # Additional blocks can be provided for
+ # data that are not enabled, but the enabled model
+ # must have a corresponding parameter block.
+ wake_turbulence_parameters:
+ crespo_hernandez:
+ initial: 0.1
+ constant: 0.5
+ ai: 0.8
+ downstream: -0.32
diff --git a/server/floris_module/src/main.py b/server/floris_module/src/main.py
new file mode 100644
index 0000000..51d8e1e
--- /dev/null
+++ b/server/floris_module/src/main.py
@@ -0,0 +1,55 @@
+import numpy as np
+import matplotlib.pyplot as plt
+
+from FlorisULSTU import FlorisULSTU
+
+
+
+# yaw_angles = np.zeros((4, 4))
+# print("Yaw angle array initialized with 0's")
+# print(yaw_angles)
+#
+# print("First turbine yawed 25 degrees for every atmospheric condition")
+# yaw_angles[:, 0] = 25
+# print(yaw_angles)
+#
+# fmodel.set(yaw_angles=yaw_angles)
+
+
+if __name__ == "__main__":
+ fmodel = FlorisULSTU("gch.yaml")
+
+ layout_x = list(map(float, input(f"Please enter x coordinates for turbines\n").split()))
+ layout_y = list(map(float, input(f"Please enter y coordinates for turbines\n").split()))
+
+ fmodel.set(
+ layout_x=layout_x, layout_y=layout_y,
+ )
+
+ wind_directions = list(map(float, input(f"Please enter wind_directions for experiments\n").split()))
+ wind_speeds = list(map(float, input(f"Please enter wind_speed for experiments\n").split()))
+
+ fmodel.set(
+ wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=[0.1] * len(wind_directions),
+ )
+
+ fmodel.run()
+
+ powers = fmodel.get_turbine_powers() / 1000.0
+
+ print("Dimensions of `powers`")
+ print(np.shape(powers))
+
+ N_TURBINES = fmodel.core.farm.n_turbines
+
+ print()
+ print("Turbine powers for 8 m/s")
+ for i in range(2):
+ print(f"Wind condition {i}")
+ for j in range(N_TURBINES):
+ print(f" Turbine {j} - {powers[i, j]:7,.2f} kW")
+ print()
+
+ print("Turbine powers for all turbines at all wind conditions")
+ print(powers)
+
diff --git a/server/main.py b/server/main.py
deleted file mode 100644
index afca163..0000000
--- a/server/main.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from fastapi import FastAPI, Depends
-from fastapi.responses import RedirectResponse
-from sqlalchemy.orm import Session
-from sqlalchemy.future import select
-from database import SessionLocal, User, Base, engine
-
-
-Base.metadata.create_all(bind=engine)
-
-app = FastAPI()
-
-
-def get_db():
- db = SessionLocal()
- try:
- yield db
- finally:
- db.close()
-
-
-@app.get("/hello")
-async def hello():
- return {"message": "Hello, World!"}
-
-
-@app.get("/")
-async def redirect_to_docs():
- return RedirectResponse(url="/docs")
-
-@app.get("/users")
-def read_users(db: Session = Depends(get_db)):
- result = db.execute(select(User))
- users = result.scalars().all() # Получаем всех пользователей
- return users
-
-if __name__ == "__main__":
- import uvicorn
- uvicorn.run(app, host="localhost", port=8080) # Изменено на localhost
diff --git a/server/src/router.py b/server/public/floris/git_init
similarity index 100%
rename from server/src/router.py
rename to server/public/floris/git_init
diff --git a/server/requirements.txt b/server/requirements.txt
index 5d438d4..8986ff1 100644
--- a/server/requirements.txt
+++ b/server/requirements.txt
@@ -24,6 +24,7 @@ PyYAML==6.0.2
rich==13.8.1
shellingham==1.5.4
sniffio==1.3.1
+SQLAlchemy==2.0.35
starlette==0.38.6
typer==0.12.5
typing_extensions==4.12.2
diff --git a/server/src/data/database.py b/server/src/data/database.py
new file mode 100644
index 0000000..01711ae
--- /dev/null
+++ b/server/src/data/database.py
@@ -0,0 +1,11 @@
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+
+# TODO Maybe create env file
+DATABASE_URL = "mysql+pymysql://wind:wind@193.124.203.110:3306/wind_towers"
+
+engine = create_engine(DATABASE_URL)
+session_maker = sessionmaker(autocommit=False, autoflush=False, bind=engine)
+
+
+
diff --git a/server/src/data/models.py b/server/src/data/models.py
new file mode 100644
index 0000000..3d1aa26
--- /dev/null
+++ b/server/src/data/models.py
@@ -0,0 +1,36 @@
+from datetime import datetime
+
+from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase
+from sqlalchemy.dialects.mysql import TIMESTAMP, TIME, VARCHAR
+
+
+class Base(DeclarativeBase):
+ pass
+
+
+class Weather(Base):
+ __tablename__ = "weather_data"
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+ BarTrend: Mapped[str] = mapped_column(VARCHAR(255))
+ CRC: Mapped[str] = mapped_column(VARCHAR(255))
+ DateStamp: Mapped[datetime] = mapped_column(TIMESTAMP)
+ DewPoint: Mapped[float]
+ HeatIndex: Mapped[float]
+ ETDay: Mapped[float]
+ HumIn: Mapped[float]
+ HumOut: Mapped[float]
+ Pressure: Mapped[float]
+ RainDay: Mapped[float]
+ RainMonth: Mapped[float]
+ RainRate: Mapped[float]
+ RainStorm: Mapped[float]
+ RainYear: Mapped[float]
+ SunRise: Mapped[datetime] = mapped_column(TIME)
+ SunSet:Mapped[datetime] = mapped_column(TIME)
+ TempIn: Mapped[float]
+ TempOut: Mapped[float]
+ WindDir: Mapped[str] = mapped_column(VARCHAR(50))
+ WindSpeed: Mapped[float]
+ WindSpeed10Min: Mapped[float]
+
diff --git a/server/src/data/repository.py b/server/src/data/repository.py
new file mode 100644
index 0000000..b9fbc39
--- /dev/null
+++ b/server/src/data/repository.py
@@ -0,0 +1,27 @@
+from sqlalchemy import select
+
+from data.database import session_maker
+from data.models import Weather
+from data.schemas import SWeatherInfo
+
+
+class WeatherRepository:
+ @classmethod
+ def get_all(cls):
+ with session_maker() as session:
+ query = (
+ select(Weather)
+ )
+ res = session.execute(query)
+ weather_models = res.scalars().all()
+ return [SWeatherInfo.model_validate(weather, from_attributes=True) for weather in weather_models]
+
+ @classmethod
+ def get_by_id(cls, id):
+ with session_maker() as session:
+ query = (
+ select(Weather).where(Weather.id == id)
+ )
+ res = session.execute(query)
+ weather_model = res.scalars().first()
+ return SWeatherInfo.model_validate(weather_model, from_attributes=True)
\ No newline at end of file
diff --git a/server/src/data/schemas.py b/server/src/data/schemas.py
new file mode 100644
index 0000000..3388350
--- /dev/null
+++ b/server/src/data/schemas.py
@@ -0,0 +1,19 @@
+from pydantic import BaseModel, Field
+from fastapi import Query
+
+
+class SWeatherInfo(BaseModel):
+ id: int
+ WindDir: str
+ WindSpeed: float
+
+
+class SFlorisInputParams(BaseModel):
+ weather_id: int
+ layout_x: list[float] = Field(Query([]))
+ layout_y: list[float] = Field(Query([]))
+
+
+class SFlorisOutputData(BaseModel):
+ file_name: str
+ data: list[float]
\ No newline at end of file
diff --git a/server/src/main.py b/server/src/main.py
new file mode 100644
index 0000000..a4a7788
--- /dev/null
+++ b/server/src/main.py
@@ -0,0 +1,21 @@
+import sys
+
+from pathlib import Path
+
+from fastapi import FastAPI
+
+from fastapi.staticfiles import StaticFiles
+
+from routers.floris_router import router as floris_router
+from routers.floris_template_router import router as floris_template_router
+from routers.weather_router import router as weather_router
+
+
+app = FastAPI()
+
+app.mount("/static", StaticFiles(directory=Path("../static")), name="static")
+app.mount("/public", StaticFiles(directory=Path("../public")), name="public")
+
+app.include_router(floris_router)
+app.include_router(floris_template_router)
+app.include_router(weather_router)
diff --git a/server/src/routers/floris_router.py b/server/src/routers/floris_router.py
new file mode 100644
index 0000000..7bbca30
--- /dev/null
+++ b/server/src/routers/floris_router.py
@@ -0,0 +1,76 @@
+import sys
+from http import HTTPStatus
+from pathlib import Path
+from typing import Annotated
+
+from fastapi import APIRouter, HTTPException, Depends
+from fastapi.responses import FileResponse
+
+from data.repository import WeatherRepository
+from data.schemas import SFlorisInputParams, SFlorisOutputData, SWeatherInfo
+
+
+sys.path.append(str(Path(__file__).parent.parent.parent))
+from floris_module.src import FlorisULSTU
+
+FLORIS_IMAGES_PATH = Path(__file__).parent.parent.parent / "public" / "floris"
+
+router = APIRouter(
+ prefix="/api/floris",
+ tags=["Floris Api"],
+)
+
+
+@router.get("/get_windmill_data")
+async def get_windmill_data(
+ data: Annotated[SFlorisInputParams, Depends()]
+):
+ if len(data.layout_x) != len(data.layout_y):
+ raise HTTPException(
+ status_code=HTTPStatus.BAD_REQUEST,
+ detail="Length of layout x and y must be the same",
+ )
+
+ atmosphere_param: SWeatherInfo = WeatherRepository().get_by_id(data.weather_id)
+
+ fmodel = FlorisULSTU()
+ fmodel.set(
+ layout_x=data.layout_x,
+ layout_y=data.layout_y,
+ wind_directions=[atmosphere_param.WindDir],
+ wind_speeds=[atmosphere_param.WindSpeed],
+ )
+ fmodel.run()
+
+ data = fmodel.get_turbine_powers()[0].tolist()
+ file_name = fmodel.visualisation()
+
+ return SFlorisOutputData(
+ file_name=file_name,
+ data=data
+ )
+
+
+@router.get("/get_images")
+async def get_images():
+ try:
+ images = [file.name for file in FLORIS_IMAGES_PATH.iterdir() if file.is_file()]
+ return {"images": images}
+ except Exception as e:
+ raise HTTPException(
+ status_code=500,
+ detail=str(e)
+ )
+
+
+@router.get("/download_image/{image_name}")
+async def download_image(
+ image_name: str
+):
+ image_path = FLORIS_IMAGES_PATH / image_name
+ if not image_path.exists() or not image_path.is_file():
+ raise HTTPException(status_code=404, detail="Image not found")
+ return FileResponse(image_path, media_type="image/jpeg", filename=image_name)
+
+
+
diff --git a/server/src/routers/floris_template_router.py b/server/src/routers/floris_template_router.py
new file mode 100644
index 0000000..1519ef8
--- /dev/null
+++ b/server/src/routers/floris_template_router.py
@@ -0,0 +1,65 @@
+from pathlib import Path
+from typing import Annotated
+
+from fastapi import APIRouter, Request, Form, Depends
+from fastapi.responses import HTMLResponse
+from fastapi.templating import Jinja2Templates
+
+from data.repository import WeatherRepository
+from data.schemas import SFlorisInputParams
+
+from routers.floris_router import get_windmill_data
+
+templates = Jinja2Templates(directory=Path("../templates/floris_templates"))
+
+router = APIRouter(
+ prefix="",
+ tags=["floris_template"],
+ include_in_schema=False,
+)
+
+
+@router.get("/", response_class=HTMLResponse)
+async def get_main(request: Request, context: dict | None = None):
+ if context is None:
+ context = {}
+
+ params = {"request": request, "weather": WeatherRepository.get_all()}
+
+ if context is not None:
+ params.update(context)
+
+ return templates.TemplateResponse("main.html", params)
+
+
+def transform_windmill_data(
+ options: Annotated[str, Form()],
+ x: Annotated[str, Form()],
+ y: Annotated[str, Form()]
+) -> SFlorisInputParams | None:
+
+ if not all([options, x, y]):
+ return None
+
+ return SFlorisInputParams(
+ weather_id=int(options),
+ layout_x=list(map(float, x.split())),
+ layout_y=list(map(float, y.split())),
+ )
+
+
+@router.post("/", response_class=HTMLResponse)
+async def post_main(
+ request: Request,
+ windmill_params: Annotated[SFlorisInputParams | None, Depends(transform_windmill_data)] = None,
+):
+ if windmill_params is None:
+ return await get_main(request)
+
+ data = await get_windmill_data(windmill_params)
+ context = {
+ 'data': data.data,
+ 'file_name': data.file_name,
+ }
+
+ return await get_main(request, context)
diff --git a/server/src/routers/weather_router.py b/server/src/routers/weather_router.py
new file mode 100644
index 0000000..01703fe
--- /dev/null
+++ b/server/src/routers/weather_router.py
@@ -0,0 +1,13 @@
+from fastapi import APIRouter
+from data.repository import WeatherRepository
+
+router = APIRouter(
+ prefix="/api/weather",
+ tags=["Weather Api"],
+)
+
+
+@router.get("/")
+async def get_weather():
+ return WeatherRepository().get_all()
+
diff --git a/server/static/style.css b/server/static/style.css
new file mode 100644
index 0000000..23851ec
--- /dev/null
+++ b/server/static/style.css
@@ -0,0 +1,67 @@
+body {
+ font-family: Arial, sans-serif;
+ background-color: #f3f4f6;
+ color: #333;
+}
+
+.container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ padding: 20px;
+}
+
+.image-viewer {
+ background-color: #fff5e6;
+ border-radius: 8px;
+ padding: 20px;
+ margin-right: 20px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+}
+
+.image-viewer img {
+ max-width: 400px;
+ max-height: 400px;
+ display: block;
+ margin-bottom: 10px;
+}
+
+.image-list {
+ background-color: #e6f7ff;
+ padding: 20px;
+ border-radius: 8px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+}
+
+.image-list ul {
+ list-style-type: none;
+ padding: 0;
+}
+
+.image-list li {
+ margin: 5px 0;
+ padding: 5px;
+ cursor: pointer;
+ background-color: #fff;
+ border-radius: 4px;
+ transition: background-color 0.3s;
+}
+
+.image-list li:hover {
+ background-color: #cceeff;
+}
+
+button {
+ background-color: #82b1ff;
+ border: none;
+ color: white;
+ padding: 10px 15px;
+ border-radius: 5px;
+ cursor: pointer;
+ margin-top: 10px;
+}
+
+button:hover {
+ background-color: #6699ff;
+}
diff --git a/server/templates/floris_templates/main.html b/server/templates/floris_templates/main.html
new file mode 100644
index 0000000..9bc3c4b
--- /dev/null
+++ b/server/templates/floris_templates/main.html
@@ -0,0 +1,85 @@
+
+
+
+
+
+ Image Viewer
+
+
+
+
+
+
+
+
+
+ {{ data }}
+ {% if data is defined %}
+
+ {% endif %}
+