You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DroneDetector/src/server_to_tablet.py

315 lines
11 KiB
Python

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import json
import os
import asyncio
import websockets
from common.runtime import load_root_env
from typing import List
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException
load_root_env(__file__)
threshold_to_alarm = int(os.getenv('threshold_to_alarm'))
time_to_jam = int(os.getenv('time_to_jam'))
time_to_fresh = int(os.getenv('time_to_fresh'))
lochost = os.getenv('lochost')
locport = int(os.getenv('locport'))
jamhost = os.getenv('jamhost')
jamport = os.getenv('jamport')
# TODO Добавить обработку кнопки и водопад.
class FreqConfig:
"""
Конфиг частот, отображаемых на планшете.
Атрибуты:
freq_config: Словарь с частотами и преднастройкой (вкл/выкл).
"""
def __init__(self):
self.freq_config = {
'433': False,
'500': False,
'700': True,
'868': True,
'915': True,
'1200': True,
'1500': False,
'2400': True,
'5200': True,
'5800': True
}
def get(self):
return self.freq_config
def get_status(self, freq):
"""
Проверка активности выбранной частоты.
:param freq: Частота для проверки.
:return: Вкл/выкл.
"""
return self.freq_config[freq]
def set_active(self, freq: str, status: bool):
"""
Переключение частоты в состояние вкл/выкл.
:param freq: Частота для отключения/включения.
:param status: Положение, в которое переключается частота (True - вкл, False - выкл).
:return: None.
"""
self.freq_config[freq] = status
app = FastAPI()
websocket_connections: List[WebSocket] = []
jam_server_connect = None
alarm = False
freqconfig = FreqConfig()
def check_active_tablets() -> None:
"""
Проверка активных соединений с каким-либо планшетом.
"""
if not websocket_connections:
raise HTTPException(status_code=400, detail="No active WebSocket connections = No tablets in sight.")
async def send_to_tablets(package) -> None:
"""
Рассылка данных по планшетам.
:param package: Собранный пакет данных для отправки.
"""
global alarm
if not websocket_connections:
print('Нет подключенных планшетов/клиентов.')
else:
for websocket in websocket_connections:
try:
print('Пытаемся отправить данные клиенту ', websocket)
await websocket.send_json(package)
print(f'Пакет {package} успешно отправлен.')
except Exception as e:
print(f"Не смогли отправить данные клиенту по вебсокету: {e}")
async def check_alarm(amplitude: int):
if amplitude > threshold_to_alarm:
return await send_jam_server_alarm()
else:
return False
# async def freq_active(freq: str):
# """
# Запуск скрина с частотой после ее активации в частотном конфиге.
# :param freq: Частота для запуска скрина.
# """
#
# # TODO добавить чек частоты в скринах, а то вдруг мы запускаем скрин, а он уже запущен.
# print(f'АКТИВИРУЕМ ЧАСТОТУ {freq}')
# command = f"screen -dmS {freq} sh -c \"export PYTHONPATH=/home/orangepi && bash -c \'python3 " \
# f"/home/orangepi/DroneScanner/src/main_5800.py\' && exec bash\""
# screen = subprocess.run(command, shell=True, capture_output=True, text=True)
#
# # Проверяем результат выполнения
# if screen.returncode == 0:
# print("Команда успешно выполнена")
# else:
# print("Ошибка при выполнении команды")
# print("STDERR:", screen.stderr)
#
#
# async def freq_deactive(freq: str):
# """
# Килл скрин с частотой после ее деактивации в частотном конфиге.
# :param freq: Частота для скрин килл.
# """
#
# print(f'ОТКЛЮЧАЕМ ЧАСТОТУ {freq}')
@app.post('/send-waterfall/')
async def send_waterfall(data: dict) -> None:
"""
Прием водопада и отсылка на планшеты, если необходимо.
:param data: Водопад в виде пакета data = {...}
"""
print('bubble tea!')
async def set_freq_config(data: dict):
"""
Переключение состояний частот пришедших с одного планшета и рассылка этой информации на остальные.
Запуск/отключение необходимых скринов.
:param data: Словарь вида {частота: состояние}
:return: None.
"""
for freq, activ in data.items():
freqconfig.set_active(freq, activ)
print(f'Частота {freq} перешла в состояние {activ}: {freqconfig.get_status(freq)}')
msg = {'type': 'freq_config',
'data': {freq: activ}}
await send_to_tablets(msg)
# if activ:
# await freq_active(freq)
#
# else:
# await freq_deactive(freq)
@app.post("/process_data")
async def send_data(data: dict):
"""
Прием данных со скриптов детекции в формате data = {"freq": freq,
"amplitude": amplitude
}
где freq - строка, amplitude - int, их обработка и рассылка по планшетам.
:param data: Словарь.
"""
global alarm
global jam_server_connect
# check_active_tablets()
print('На сервер пришли данные: ', data)
if alarm and jam_server_connect:
print('Подавитель активен.')
return {'message': 'Подавитель активен.'}
elif alarm and jam_server_connect is None:
alarm = False
if not freqconfig.get_status(data['freq']):
print('Частота выключена.')
return {'message': 'Частота выключена.'}
if await check_alarm(data['amplitude']):
print('Затриггерились')
msg = {'type': 'freq',
'data': data
}
await send_to_tablets(msg)
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket) -> None:
"""
Прием данных (freq config) по вебсокету от клиента (планшетов) и их обработка.
При подключении к серверу - отсылка на клиента текущее состояние частотного конфига.
:param websocket:
:return:
"""
await websocket.accept()
websocket_connections.append(websocket)
msg = {'type': 'freq_config',
'data': freqconfig.get()
}
await websocket.send_json(msg)
try:
while True:
try:
data_from_client = await websocket.receive_json() # Ожидание получения данных от клиента
if 'freq' in data_from_client:
data = data_from_client['freq']
print('Приняли с планшета: ', data)
await set_freq_config(data)
except WebSocketDisconnect:
print("Client disconnected")
websocket_connections.remove(websocket)
break
except Exception as e:
print(f"Error receiving data: {e}")
except WebSocketDisconnect:
websocket_connections.remove(websocket)
# print(e)
# print(f"Client disconnected: {e.code} - {e.reason}")
##################################################################################
# Подключение к серверу глушилок, как клиент по вебсокету и обработка информации.
##################################################################################
async def jammer_active():
"""
Активируем подавитель.
"""
global alarm
print('АКТИВИРУЕМ ПОДАВИТЕЛЬ!!!!')
alarm = True
async def jammer_deactive():
"""
Отключаем подавитель.
"""
global alarm
print('ОТКЛЮАЕМ ПОДАВИТЕЛЬ!!!!')
alarm = False
async def send_jam_server_alarm():
"""
Отправить пакет с триггером на сервер подавителей.
:return: True, если соединение с сервером активно и данные отправлены успешно. False - иначе.
"""
global jam_server_connect
msg = {'type': 'freq_alarm',
'data': True}
if jam_server_connect:
await jam_server_connect.send(json.dumps(msg))
return True
else:
return False
async def jam_server():
"""
Подключиться к серверу подавителей по вебсокету как клиент. Получение и обработка пакетов - активация/деактивация
подавителя.
"""
uri = f'ws://{jamhost}:{jamport}/ws'
global jam_server_connect
while True:
try:
jam_server_connect = await websockets.connect(uri)
while True:
data_from_jam_server = await jam_server_connect.recv()
data_from_jam_server = json.loads(data_from_jam_server)
print('Принял с сервера глушилок: ', data_from_jam_server)
if data_from_jam_server['type'] == 'run':
alarm_status = (data_from_jam_server['data'])['state']
print(alarm_status)
if alarm_status:
await jammer_active()
else:
await jammer_deactive()
except Exception as e:
jam_server_connect = None
if alarm:
await jammer_deactive()
@app.on_event("startup")
async def startup_event():
"""
Запуск подключения к серверу подавления и получения от него сообщений.
"""
asyncio.create_task(jam_server())
##################################################################################
# Запуск приложения.
##################################################################################
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host=lochost, port=locport)