python microservice
This commit is contained in:
parent
4871f9e5b9
commit
4d8cde2032
1
src/microservices/py-simulation/.dockerignore
Normal file
1
src/microservices/py-simulation/.dockerignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
.venv
|
16
src/microservices/py-simulation/Dockerfile
Normal file
16
src/microservices/py-simulation/Dockerfile
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Используем официальный образ Python
|
||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
# Устанавливаем рабочую директорию внутри контейнера
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Копируем requirements в контейнер и устанавливаем зависимости
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Копируем весь код в контейнер
|
||||||
|
COPY . .
|
||||||
|
# Задаем переменные окружения для лучшего управления
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
# Команда для запуска приложения
|
||||||
|
CMD ["python", "main.py"]
|
133
src/microservices/py-simulation/main.py
Normal file
133
src/microservices/py-simulation/main.py
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import numpy as np
|
||||||
|
import json
|
||||||
|
import pywavefront
|
||||||
|
import asyncio
|
||||||
|
import websockets
|
||||||
|
from fastapi import FastAPI, HTTPException
|
||||||
|
from pydantic import BaseModel
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
class SimulationRequest(BaseModel):
|
||||||
|
map_obj_path: str
|
||||||
|
drones_data: str
|
||||||
|
base_stations_data: str
|
||||||
|
simulation_time: int
|
||||||
|
websocket_url: str
|
||||||
|
|
||||||
|
class DroneNetworkSimulator:
|
||||||
|
def __init__(self, map_obj_path, drones_data, base_stations_data, simulation_time, websocket_url):
|
||||||
|
self.map_data = self.load_map(map_obj_path)
|
||||||
|
self.drones = self.load_drones(drones_data)
|
||||||
|
self.base_stations = self.load_base_stations(base_stations_data)
|
||||||
|
self.simulation_time = simulation_time
|
||||||
|
self.websocket_url = websocket_url
|
||||||
|
self.time_series_data = []
|
||||||
|
|
||||||
|
def load_map(self, map_obj_path):
|
||||||
|
# Используем pywavefront для загрузки .obj карты
|
||||||
|
scene = pywavefront.Wavefront(map_obj_path, collect_faces=True)
|
||||||
|
vertices = np.array(scene.vertices)
|
||||||
|
# Извлекаем высоты и используем минимальное количество вершин для представления высотной карты
|
||||||
|
z_values = vertices[:, 2]
|
||||||
|
grid_size = int(np.sqrt(len(z_values)))
|
||||||
|
map_data = z_values[:grid_size ** 2].reshape((grid_size, grid_size)) # Формируем карту с учетом размера данных
|
||||||
|
return map_data
|
||||||
|
|
||||||
|
def load_drones(self, drones_data):
|
||||||
|
return json.loads(drones_data)
|
||||||
|
|
||||||
|
def load_base_stations(self, base_stations_data):
|
||||||
|
return json.loads(base_stations_data)
|
||||||
|
|
||||||
|
async def simulate(self):
|
||||||
|
async with websockets.connect(self.websocket_url) as websocket:
|
||||||
|
for t in range(self.simulation_time):
|
||||||
|
current_state = []
|
||||||
|
for drone in self.drones:
|
||||||
|
self.update_drone_position(drone)
|
||||||
|
network_state = self.calculate_network_state(drone)
|
||||||
|
current_state.append({
|
||||||
|
"drone_id": drone["id"],
|
||||||
|
"position": drone["position"],
|
||||||
|
"network_state": network_state,
|
||||||
|
"connected": network_state["connected_base_station"] is not None,
|
||||||
|
"network_speed": self.calculate_network_speed(network_state["signal_strength"])
|
||||||
|
})
|
||||||
|
self.time_series_data.append({"time": t, "state": current_state})
|
||||||
|
await websocket.send(json.dumps({"time": t, "state": current_state}))
|
||||||
|
|
||||||
|
def update_drone_position(self, drone):
|
||||||
|
# Обновление позиции дрона в зависимости от целевых точек следования
|
||||||
|
if "waypoints" in drone and drone["waypoints"]:
|
||||||
|
target = np.array(drone["waypoints"][0])
|
||||||
|
current_position = np.array(drone["position"])
|
||||||
|
direction = target - current_position
|
||||||
|
distance = np.linalg.norm(direction)
|
||||||
|
if distance <= drone.get("speed", 1):
|
||||||
|
# Достигнута целевая точка
|
||||||
|
drone["position"] = target.tolist()
|
||||||
|
drone["waypoints"].pop(0) # Удаляем достигнутую точку
|
||||||
|
else:
|
||||||
|
# Двигаемся в направлении целевой точки
|
||||||
|
direction = direction / distance # Нормализуем направление
|
||||||
|
new_position = current_position + direction * drone.get("speed", 1)
|
||||||
|
drone["position"] = new_position.tolist()
|
||||||
|
|
||||||
|
def calculate_network_state(self, drone):
|
||||||
|
# Рассчитываем состояние сети, учитывая препятствия, частоту и радиус базовых станций
|
||||||
|
position = np.array(drone["position"])
|
||||||
|
frequency = drone.get("frequency", 2.4) # Частота в GHz, по умолчанию 2.4 GHz
|
||||||
|
path_loss_exponent = drone.get("path_loss_exponent", 2.5) # Показатель затухания, заданный в JSON
|
||||||
|
obstacles_factor = self.calculate_obstacles_factor(position)
|
||||||
|
distance = np.linalg.norm(position)
|
||||||
|
signal_strength = 100 / (1 + (distance ** path_loss_exponent) * obstacles_factor * (1 / frequency))
|
||||||
|
|
||||||
|
# Проверяем подключение к базовой станции
|
||||||
|
connected_base_station = None
|
||||||
|
for base_station in self.base_stations:
|
||||||
|
base_position = np.array(base_station["position"])
|
||||||
|
base_radius = base_station.get("radius", 50) # Радиус действия базовой станции по умолчанию 50
|
||||||
|
base_distance = np.linalg.norm(position - base_position)
|
||||||
|
if base_distance <= base_radius:
|
||||||
|
connected_base_station = base_station["id"]
|
||||||
|
break
|
||||||
|
|
||||||
|
return {
|
||||||
|
"signal_strength": signal_strength,
|
||||||
|
"connected_base_station": connected_base_station
|
||||||
|
}
|
||||||
|
|
||||||
|
def calculate_network_speed(self, signal_strength):
|
||||||
|
# Рассчитываем скорость сети в зависимости от уровня сигнала (упрощенная модель)
|
||||||
|
max_speed = 100.0 # Максимальная скорость сети в Mbps
|
||||||
|
return max_speed * (signal_strength / 100)
|
||||||
|
|
||||||
|
def calculate_obstacles_factor(self, position):
|
||||||
|
# Фактор, учитывающий препятствия на карте, влияющие на затухание
|
||||||
|
x, y, z = int(position[0]), int(position[1]), int(position[2])
|
||||||
|
if 0 <= x < self.map_data.shape[0] and 0 <= y < self.map_data.shape[1]:
|
||||||
|
height_at_point = self.map_data[x, y]
|
||||||
|
if z < height_at_point:
|
||||||
|
# Если дрон находится ниже препятствия, затухание выше
|
||||||
|
return 5.0
|
||||||
|
return 1.0
|
||||||
|
|
||||||
|
@app.post("/start_simulation")
|
||||||
|
async def start_simulation(request: SimulationRequest):
|
||||||
|
try:
|
||||||
|
simulator = DroneNetworkSimulator(
|
||||||
|
request.map_obj_path,
|
||||||
|
request.drones_data,
|
||||||
|
request.base_stations_data,
|
||||||
|
request.simulation_time,
|
||||||
|
request.websocket_url
|
||||||
|
)
|
||||||
|
await simulator.simulate()
|
||||||
|
return {"status": "Simulation started and results are being sent via WebSocket."}
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=10000)
|
2
src/microservices/py-simulation/map.mtl
Normal file
2
src/microservices/py-simulation/map.mtl
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Blender 4.2.3 LTS MTL File: 'None'
|
||||||
|
# www.blender.org
|
474394
src/microservices/py-simulation/map.obj
Normal file
474394
src/microservices/py-simulation/map.obj
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user