|
|
# #! /usr/bin/env python
|
|
|
# # -*- coding: utf-8 -*-
|
|
|
# import os
|
|
|
# import json
|
|
|
# import httpx
|
|
|
# import asyncio
|
|
|
# import requests
|
|
|
# import websockets
|
|
|
# from copy import deepcopy
|
|
|
# from fastapi import FastAPI
|
|
|
# from dotenv import load_dotenv
|
|
|
# from datetime import datetime, timedelta
|
|
|
# import csv
|
|
|
|
|
|
# app = FastAPI()
|
|
|
|
|
|
# ############################################################################
|
|
|
# # VARIABLES
|
|
|
# ############################################################################
|
|
|
# dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
|
|
|
# load_dotenv(dotenv_path)
|
|
|
# lochost = os.getenv('lochost')
|
|
|
# locport = os.getenv('locport')
|
|
|
# jamhost = os.getenv('jamhost')
|
|
|
# jamport = os.getenv('jamport')
|
|
|
# master_server_ip = os.getenv('master_server_ip')
|
|
|
# master_server_port = os.getenv('master_server_port')
|
|
|
|
|
|
# freqs = [str(x) for x in os.getenv('freqs').split(',')]
|
|
|
# num_of_clear_packs = int(os.getenv('num_of_clear_packs'))
|
|
|
# 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'))
|
|
|
# active_interval_to_send = int(os.getenv('active_interval_to_send'))
|
|
|
# passive_interval_to_send = int(os.getenv('passive_interval_to_send'))
|
|
|
# jammer_timeout = int(os.getenv('jammer_timeout'))
|
|
|
# master_timeout = int(os.getenv('master_timeout'))
|
|
|
# amount_connection_attempts = int(os.getenv('amount_connection_attempts'))
|
|
|
|
|
|
# debug_module_flag = bool(os.getenv('debug_module_flag'))
|
|
|
# send_to_module_flag = bool(os.getenv('send_to_module_flag'))
|
|
|
# send_to_master_flag = bool(os.getenv('send_to_master_flag'))
|
|
|
# send_to_jammer_flag = bool(os.getenv('send_to_jammer_flag'))
|
|
|
|
|
|
# latitude = float(os.getenv('latitude'))
|
|
|
# longitude = float(os.getenv('longitude'))
|
|
|
|
|
|
|
|
|
# flag = 0
|
|
|
# max_len_bulk = 1
|
|
|
# bulk_data = []
|
|
|
|
|
|
# sending_data_task = None
|
|
|
# jam_server_connect = None
|
|
|
|
|
|
# alarm = False
|
|
|
# alarm_list = []
|
|
|
# jammer_event = False
|
|
|
|
|
|
# data_queue = [None] * len(freqs)
|
|
|
# freqs_alarm = {freq: 0 for freq in freqs}
|
|
|
|
|
|
|
|
|
# #TODO:
|
|
|
# # 1. Вырезать flag, если нужно и возможно.
|
|
|
# # 2. Пофиксить костыль с asyncio.create_task(sending_data) - после jammer_event'a перестает подавать признаки жизни,
|
|
|
# # поэтому гасим, когда включается глушилка и запускаем заново, когда глушилка отключается.
|
|
|
# # 3. Пофиксить момент с data_queue:из-за асинхронных функций и старой реализации сервака происходит так ,что
|
|
|
# # прилетает пакет аларма, система это видит, хочет его отправить, начинает отправку и из-за того, что это немного
|
|
|
# # долгий процесс, то успевает прилететь чистый пакет и на мастер улетает чистый пакет.
|
|
|
# # 4. Добавить print, только если deub_module_flag.
|
|
|
|
|
|
|
|
|
# ############################################################################
|
|
|
# # GPS MODULE - INACTIVE
|
|
|
# ############################################################################
|
|
|
# # Создание планировщика
|
|
|
# # scheduler = BackgroundScheduler(daemon=True)
|
|
|
# # scheduler.start()
|
|
|
# # @app.route('/get_gps', methods = ['POST'])
|
|
|
# # @scheduler.scheduled_job(IntervalTrigger(minutes=1))
|
|
|
# # def update_gps_coordinates():
|
|
|
# # # data_gps = request.json
|
|
|
# # result = {
|
|
|
# # 'latitude': latitude,
|
|
|
# # 'longitude': longitude
|
|
|
# # }
|
|
|
# # try:
|
|
|
# # url = "http://{0}:{1}/data/gps/{2}".format(master_server_ip, master_server_port, mac_address)
|
|
|
# # response = requests.post(url, json=result)
|
|
|
# # if response.status_code == 200:
|
|
|
# # print('gps успешно отправлен')
|
|
|
# # else:
|
|
|
# # print('gps не был отправлен')
|
|
|
# #
|
|
|
# # except Exception:
|
|
|
# # print('gps не были отправлены из-за отстутствия сервера в поле видимости')
|
|
|
# # return result
|
|
|
# #
|
|
|
# #
|
|
|
# # @scheduler.scheduled_job(IntervalTrigger(seconds=10))
|
|
|
# # def send_gps_to_master():
|
|
|
# # try:
|
|
|
# # subprocess.run(["python3", "GPS_get_coords.py"])
|
|
|
# # #mac_address = get_mac_address()
|
|
|
# # data_gps = update_gps_coordinates()
|
|
|
# # url = "http://{0}:{1}/data/gps/{2}".format(master_server_ip, master_server_port, mac_address)
|
|
|
# # response = requests.post(url, json=data_gps)
|
|
|
# # if response.status_code == 200:
|
|
|
# # print('gps успешно отправлен')
|
|
|
# # else:
|
|
|
# # print('gps не был отправлены по какой-то причине')
|
|
|
# # except Exception:
|
|
|
# # print('gps не были отправлены по причине отсутствия сервера в поле видимости')
|
|
|
# #
|
|
|
|
|
|
|
|
|
# ############################################################################
|
|
|
# # MODULE RIGISTR
|
|
|
# ############################################################################
|
|
|
# def get_mac_address(interface='eth0'):
|
|
|
# """
|
|
|
# Получить мак текущего устройства, на котором развернут модуль сервер.
|
|
|
# :param interface:
|
|
|
# """
|
|
|
# try:
|
|
|
# result = os.popen('sudo ifconfig ' + interface).read()
|
|
|
# mac_index = result.find('ether') # Индекс начала строки с MAC-адресом
|
|
|
# if mac_index != -1:
|
|
|
# mac_address = result[mac_index + 6:mac_index + 23]
|
|
|
# return mac_address
|
|
|
# else:
|
|
|
# return None
|
|
|
# except Exception as e:
|
|
|
# print("Ошибка при получении MAC-адреса:" + str(e))
|
|
|
# return None
|
|
|
|
|
|
|
|
|
# def get_ip_address(interface='eth0'):
|
|
|
# """
|
|
|
# Получить айпишник текущего устройства, на котором развернут модуль сервер.
|
|
|
# :param interface:
|
|
|
# """
|
|
|
# try:
|
|
|
# result = os.popen('sudo ifconfig ' + interface).read()
|
|
|
# ip_index = result.find('inet') # Индекс начала строки с IP-адресом
|
|
|
# if ip_index != -1:
|
|
|
# ip_address = result[ip_index + 5:ip_index + 17]
|
|
|
# return ip_address.strip()
|
|
|
# else:
|
|
|
# return None
|
|
|
# except Exception as e:
|
|
|
# print("Ошибка при получении IP-адреса:" + str(e))
|
|
|
# return None
|
|
|
|
|
|
|
|
|
# def register_module():
|
|
|
# """
|
|
|
# Регистрация модуля на мастер сервере.
|
|
|
# """
|
|
|
|
|
|
# data = {'mac': get_mac_address(),
|
|
|
# 'ip': get_ip_address(),
|
|
|
# 'moduleType': 'freq'}
|
|
|
# try:
|
|
|
# url = f"http://{master_server_ip}:{master_server_port}/module/register"
|
|
|
# response = requests.post(url, json=data)
|
|
|
# response.raise_for_status() # Проверка успешности запроса
|
|
|
# print("Модуль зарегистрирован успешно = ", data)
|
|
|
# except requests.exceptions.RequestException as e:
|
|
|
# flag = 1
|
|
|
# print("Ошибка при регистрации модуля:" + str(e), data)
|
|
|
|
|
|
|
|
|
# ############################################################################
|
|
|
# # SEND DATA TO MASTER
|
|
|
# ############################################################################
|
|
|
# async def send_to_master(ModuleDataSingleV2, flag):
|
|
|
# """
|
|
|
# Отправка данных на мастер по посту или через булк. По посту установлен лимит времени на отправку. В случае его
|
|
|
# превышения - данные не отправлены. В случа неудачи отправки по любому из методов - данные не отправлены.
|
|
|
# :param ModuleDataSingleV2: Пакет данных.
|
|
|
# :param flag:
|
|
|
# :return:
|
|
|
# """
|
|
|
# mac_address = get_mac_address()
|
|
|
# async with httpx.AsyncClient() as client:
|
|
|
# for _ in range(amount_connection_attempts):
|
|
|
# try:
|
|
|
# if flag == 0:
|
|
|
# url = f"http://{master_server_ip}:{master_server_port}/data/single/{mac_address}"
|
|
|
# else:
|
|
|
# url = f"http://{master_server_ip}:{master_server_port}/data/bulk/{mac_address}"
|
|
|
# response = await client.post(url, json=ModuleDataSingleV2, timeout=master_timeout)
|
|
|
# if response.status_code == 200:
|
|
|
# print('Данные успешно отправлены')
|
|
|
# flag = 0
|
|
|
# bulk_data.clear()
|
|
|
# return 0
|
|
|
# else:
|
|
|
# flag = 1
|
|
|
# if len(bulk_data) > max_len_bulk: # Если лимит bulk_data превышен, то удаляем первый элемент списка
|
|
|
# bulk_data.pop(0)
|
|
|
# bulk_data.append(ModuleDataSingleV2)
|
|
|
# print('Данные не были отправлены по какой-то причине')
|
|
|
# except Exception as e:
|
|
|
# if len(bulk_data) > max_len_bulk: # Если лимит bulk_data превышен, то удаляем первый элемент списка
|
|
|
# bulk_data.pop(0)
|
|
|
# bulk_data.append(ModuleDataSingleV2)
|
|
|
# flag = 1
|
|
|
# print('*'*100)
|
|
|
# print('Данные не были отправлены по причине отсутствия сервера в поле видимости')
|
|
|
|
|
|
|
|
|
# ############################################################################
|
|
|
# # PROCESS DATA
|
|
|
# ############################################################################
|
|
|
# async def check_alarm(amplitude: int):
|
|
|
# """
|
|
|
# Проверка амплитуды на превышение границы отработки системы.
|
|
|
# :param amplitude: Амплитуда.
|
|
|
# :return: Превышает/не превышает.
|
|
|
# """
|
|
|
# if amplitude > threshold_to_alarm:
|
|
|
# return True
|
|
|
# else:
|
|
|
# return False
|
|
|
|
|
|
|
|
|
# async def agregate_data(data_to_agregate: list):
|
|
|
# """
|
|
|
# Сбор пакета для отправки на мастер сервер.
|
|
|
# :param data_to_agregate: Список из частотных пакетов. Длина списка = количесту обнаруживаемых частот. Может
|
|
|
# содержать None, если частота ничего не присылает на модуль сервер.
|
|
|
# :return: Пакет данных для отправки на мастер.
|
|
|
# """
|
|
|
|
|
|
# data = []
|
|
|
|
|
|
# if any(item is not None for item in data_to_agregate):
|
|
|
# for item in data_to_agregate:
|
|
|
# if item is not None:
|
|
|
# item['freq']=int(item['freq'])
|
|
|
# data.append(item)
|
|
|
|
|
|
# now = datetime.utcnow() - timedelta(seconds=2)
|
|
|
# now = now.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
# data = {
|
|
|
# "registeredAt": now,
|
|
|
# "data": data
|
|
|
# }
|
|
|
|
|
|
# for i in range(len(freqs)):
|
|
|
# data_queue[i] = None
|
|
|
|
|
|
# return data
|
|
|
|
|
|
|
|
|
# async def sending_data():
|
|
|
|
|
|
# #TODO: Надо по-хорошему нормально эту функцию переписать
|
|
|
# """
|
|
|
# Отправка пакета данных на мастер сервер раз в некоторое время в определенном формате. Время отправки зависит
|
|
|
# от текущего статуса тревоги (аларм/не аларм).
|
|
|
# """
|
|
|
|
|
|
# global alarm
|
|
|
# global jammer_event
|
|
|
# global alarm_list
|
|
|
|
|
|
# while True:
|
|
|
# print('while true!')
|
|
|
# print(data_queue)
|
|
|
|
|
|
|
|
|
# # Если перед отправкой на мастер все было чисто, то ждем 60 сек.
|
|
|
# # Если во время этих 60 сек. пришел пакет с алармом, то рассматриваем ситуации:
|
|
|
# if not alarm:
|
|
|
# alarm_list = []
|
|
|
# ModuleDataSingleV2 = await agregate_data(deepcopy(data_queue))
|
|
|
# print(f'На Мастер будет отправлена следующая информация: {ModuleDataSingleV2}')
|
|
|
# await send_to_master(ModuleDataSingleV2, flag)
|
|
|
|
|
|
# for i in range(passive_interval_to_send, 0, -1):
|
|
|
# print('ТАЙМЕР ', i)
|
|
|
# await asyncio.sleep(1)
|
|
|
# if alarm:
|
|
|
# break
|
|
|
|
|
|
# # Если стоит флаг отправить данные на джеммер и при этом еще не был получен ивент на глушилку, то
|
|
|
# # отправляем на джеммер данные.
|
|
|
# else:
|
|
|
# print(alarm_list)
|
|
|
# data = deepcopy(data_queue)
|
|
|
# for i in alarm_list:
|
|
|
# try:
|
|
|
# p = list(map(lambda x: str(x['freq']), data)).index(i)
|
|
|
# print(p)
|
|
|
# data[p]['amplitude'] = 9
|
|
|
# data[p]['triggered'] = True
|
|
|
# except ValueError:
|
|
|
# data.append({'freq': i, 'amplitude': 9, 'triggered': True})
|
|
|
|
|
|
# print(data)
|
|
|
|
|
|
# alarm_list = []
|
|
|
# ModuleDataSingleV2 = await agregate_data(deepcopy(data))
|
|
|
# print(f'На Мастер будет отправлена следующая информация: {ModuleDataSingleV2}')
|
|
|
# await send_to_master(ModuleDataSingleV2, flag)
|
|
|
|
|
|
# if await send_jam_server_alarm():
|
|
|
# print('Отправили на сервис подавления и все дошло успешно')
|
|
|
# else:
|
|
|
# print('Не смогли отправить на сервис подавления')
|
|
|
# continue
|
|
|
|
|
|
# # В случае аларма ждем секунду перед новой отправкой данных.
|
|
|
# await asyncio.sleep(active_interval_to_send)
|
|
|
|
|
|
|
|
|
# @app.post('/waterfall')
|
|
|
# async def waterfall(data: dict):
|
|
|
# print('Received data: ', data)
|
|
|
|
|
|
|
|
|
# @app.post('/process_data')
|
|
|
# async def process_data(data: dict):
|
|
|
# """
|
|
|
# Прием данных со скриптов детекции в формате data = {"freq": freq,
|
|
|
# "amplitude": amplitude
|
|
|
# }
|
|
|
# где freq - строка, amplitude - int и их первичная обработка.
|
|
|
# :param data: словарь с двумя ключами и значениями.
|
|
|
# """
|
|
|
|
|
|
# global alarm
|
|
|
# global alarm_list
|
|
|
|
|
|
# print('Received data: ', data)
|
|
|
# data_dict = deepcopy(data)
|
|
|
|
|
|
# # Агрегируем N пакетов данных от частот в один общий список, он используется в функции agregate data.
|
|
|
# # Каждая позиция списка фиксируется за отдельной частотой.
|
|
|
# freq = data_dict['freq']
|
|
|
# for i in range(len(freqs)):
|
|
|
# if freq == freqs[i]:
|
|
|
# #Так делаем потому, что сервак является центром принятия решений по триггеру.
|
|
|
# trigger = await check_alarm(data_dict['amplitude'])
|
|
|
# data_dict.update({'triggered': trigger})
|
|
|
|
|
|
# data_queue[i] = deepcopy(data_dict)
|
|
|
# data_dict.clear()
|
|
|
|
|
|
# # Если прилетел триггер и глушилка не включена, то запускаем/обновляем счетчик чистых пакетов на этой
|
|
|
# # частоте.
|
|
|
# if trigger and not jammer_event:
|
|
|
# freqs_alarm[freq] = num_of_clear_packs
|
|
|
# print(f'freqs_alarm выглядит следующим обазом: {freqs_alarm}')
|
|
|
|
|
|
# #Если прилетел триггер и модуль еще не заалармлен, то алармим.
|
|
|
# if trigger and not alarm:
|
|
|
# print('Приелет триггерa со сканнера. Работаем, ребята!')
|
|
|
# alarm = True
|
|
|
# alarm_list.append(str(freq))
|
|
|
|
|
|
# # Если прилетел триггер и модуль заалармлен, но при этом глушилка не работает и счетчик чистых пакетов
|
|
|
# # данной частоты не равен нулю, то уменьшаем его. А когда в словаре счетчиков все нули, то убираем alarm.
|
|
|
# elif not trigger and alarm and not jammer_event and freqs_alarm[freq] != 0:
|
|
|
# freqs_alarm[freq] -= 1
|
|
|
# print(f'Чистый пакет. Уменьшаем в выбранной частоте: {freqs_alarm}')
|
|
|
|
|
|
# if all(value == 0 for value in freqs_alarm.values()):
|
|
|
# alarm = False
|
|
|
# print(f'Прилетело {num_of_clear_packs}. Отключаем аларм и freqs_alarm выглядит так: {freqs_alarm}')
|
|
|
|
|
|
# else:
|
|
|
# continue
|
|
|
|
|
|
# print('После получения данных data_queue выглядит следующим образом: ', data_queue)
|
|
|
|
|
|
|
|
|
# ############################################################################
|
|
|
# # JAMMER
|
|
|
# ############################################################################
|
|
|
# async def jammer_active():
|
|
|
# """
|
|
|
# Включение подавителя.
|
|
|
# Отменяем таску на отправку данных на мастер. Зануляем словарь чистых пакетов и объявляем о прилете ивента с сервиса
|
|
|
# подавления.
|
|
|
# """
|
|
|
|
|
|
# global jammer_event
|
|
|
# global freqs_alarm
|
|
|
# global sending_data_task
|
|
|
|
|
|
# if sending_data_task is not None:
|
|
|
# sending_data_task.cancel()
|
|
|
|
|
|
# freqs_alarm = {freq: 0 for freq in freqs}
|
|
|
# # with open('university_records.csv', 'w', newline='') as csvfile:
|
|
|
# # fieldnames = ['freq', 'branch', 'year', 'cgpa']
|
|
|
# # writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
|
|
# # writer.writeheader()
|
|
|
# # writer.writerows(data)
|
|
|
# jammer_event = True
|
|
|
|
|
|
# print('АКТИВИРУЕМ ПОДАВИТЕЛЬ ААААААААААААААААААААААААААААААААААААААААААААААА!!!!')
|
|
|
# print('-' * 20)
|
|
|
# print('Статус по переменным:')
|
|
|
# print(f'freqs_alarm: {freqs_alarm}')
|
|
|
# print(f'jammer_event: {jammer_event}')
|
|
|
# print(f'alarm: {alarm}')
|
|
|
# print('-' * 20)
|
|
|
|
|
|
|
|
|
# async def jammer_deactive():
|
|
|
# """
|
|
|
# Отключение подавителя.
|
|
|
# Отрубаем аларм на модуле, отрубаем ивент сервера подавителей и запускаем таску отправки данных на мастер.
|
|
|
# :return:
|
|
|
# """
|
|
|
|
|
|
# global jammer_event
|
|
|
# global alarm
|
|
|
# global sending_data_task
|
|
|
# alarm = False
|
|
|
# jammer_event = False
|
|
|
# sending_data_task = asyncio.create_task(sending_data())
|
|
|
|
|
|
# print('ОТКЛЮЧАЕМ ПОДАВИТЕЛЬ ААААААААААААААААААААААААААААААААААААААААААААААААА!!!!')
|
|
|
# print('-' * 20)
|
|
|
# print('Статус по переменным:')
|
|
|
# print(f'freqs_alarm: {freqs_alarm}')
|
|
|
# print(f'jammer_event: {jammer_event}')
|
|
|
# print(f'alarm: {alarm}')
|
|
|
# print('-' * 20)
|
|
|
|
|
|
|
|
|
# async def send_jam_server_alarm():
|
|
|
# """
|
|
|
# Отправка алармовского пакета на сервер подавления по вебсокету. На отправку дается jammer_timeout секунд.
|
|
|
# При неудаче - данные не отправлены.
|
|
|
# """
|
|
|
|
|
|
# global jam_server_connect
|
|
|
|
|
|
# msg = {'type': 'freq_alarm',
|
|
|
# 'data': True}
|
|
|
|
|
|
# # if jam_server_connect:
|
|
|
# # try:
|
|
|
# # await jam_server_connect.send(json.dumps(msg))
|
|
|
# # await asyncio.wait_for(jam_server_connect.recv(), jammer_timeout)
|
|
|
# # return True
|
|
|
# # except (asyncio.TimeoutError, websockets.exceptions.WebSocketException) as e:
|
|
|
# # print(f"WebSocket error or timeout: {e}")
|
|
|
# # return False
|
|
|
# # else:
|
|
|
# # return False
|
|
|
|
|
|
# async with httpx.AsyncClient() as client:
|
|
|
# try:
|
|
|
# url = f"http://{jamhost}:{jamport}/jammer/unsafe_run"
|
|
|
# response = await client.post(url, timeout=jammer_timeout)
|
|
|
# if response.status_code in [200, 208]:
|
|
|
# print('Данные успешно отправлены')
|
|
|
# return True
|
|
|
# else:
|
|
|
# print('Данные не были отправлены по какой-то причине')
|
|
|
# except (httpx.RequestError, asyncio.TimeoutError) as e:
|
|
|
# print('Данные не были отправлены по причине отсутствия сервера в поле видимости')
|
|
|
# except Exception as e:
|
|
|
# print('Ошибка ', str(e))
|
|
|
|
|
|
|
|
|
# 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 asyncio.sleep(5)
|
|
|
# await jammer_deactive()
|
|
|
# except Exception as e:
|
|
|
# jam_server_connect = None
|
|
|
# if jammer_event:
|
|
|
# await jammer_deactive()
|
|
|
|
|
|
|
|
|
# @app.on_event("startup")
|
|
|
# async def startup_event():
|
|
|
# """
|
|
|
# Запускаем параллельно задачи jam_server и sending_data.
|
|
|
# """
|
|
|
|
|
|
# global sending_data_task
|
|
|
# asyncio.create_task(jam_server())
|
|
|
# sending_data_task = asyncio.create_task(sending_data())
|
|
|
|
|
|
|
|
|
# if __name__ == '__main__':
|
|
|
# import uvicorn
|
|
|
# # update_gps_coordinates()
|
|
|
# register_module() # Регистрация модуля на сервере
|
|
|
# uvicorn.run(app, host=lochost, port=int(locport))
|
|
|
|
|
|
|
|
|
#! /usr/bin/env python
|
|
|
# -*- coding: utf-8 -*-
|
|
|
import os
|
|
|
import json
|
|
|
import httpx
|
|
|
import uvicorn
|
|
|
import asyncio
|
|
|
import requests
|
|
|
import websockets
|
|
|
from copy import deepcopy
|
|
|
from fastapi import FastAPI
|
|
|
from dotenv import load_dotenv
|
|
|
from datetime import datetime, timedelta
|
|
|
import warnings
|
|
|
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
|
|
|
|
app = FastAPI()
|
|
|
|
|
|
############################################################################
|
|
|
# VARIABLES
|
|
|
############################################################################
|
|
|
dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
|
|
|
load_dotenv(dotenv_path)
|
|
|
lochost = os.getenv('lochost')
|
|
|
locport = os.getenv('locport')
|
|
|
jamhost = os.getenv('jamhost')
|
|
|
jamport = os.getenv('jamport')
|
|
|
master_server_ip = os.getenv('master_server_ip')
|
|
|
master_server_port = os.getenv('master_server_port')
|
|
|
|
|
|
freqs = [str(x) for x in os.getenv('freqs').split(',')]
|
|
|
num_of_clear_packs = int(os.getenv('num_of_clear_packs'))
|
|
|
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'))
|
|
|
active_interval_to_send = int(os.getenv('active_interval_to_send'))
|
|
|
passive_interval_to_send = int(os.getenv('passive_interval_to_send'))
|
|
|
jammer_timeout = int(os.getenv('jammer_timeout'))
|
|
|
master_timeout = int(os.getenv('master_timeout'))
|
|
|
|
|
|
debug_module_flag = bool(os.getenv('debug_module_flag'))
|
|
|
send_to_module_flag = bool(os.getenv('send_to_module_flag'))
|
|
|
send_to_master_flag = bool(os.getenv('send_to_master_flag'))
|
|
|
send_to_jammer_flag = bool(os.getenv('send_to_jammer_flag'))
|
|
|
|
|
|
latitude = float(os.getenv('latitude'))
|
|
|
longitude = float(os.getenv('longitude'))
|
|
|
|
|
|
|
|
|
p = 1
|
|
|
flag = 0
|
|
|
max_len_bulk = 1
|
|
|
bulk_data = []
|
|
|
|
|
|
sending_data_task = None
|
|
|
jam_server_connect = None
|
|
|
|
|
|
alarm = False
|
|
|
jammer_event = False
|
|
|
|
|
|
data_queue = [None] * len(freqs)
|
|
|
freqs_alarm = {freq: 0 for freq in freqs}
|
|
|
|
|
|
|
|
|
#TODO:
|
|
|
# 1. Вырезать flag, если нужно и возможно.
|
|
|
# 2. Пофиксить костыль с asyncio.create_task(sending_data) - после jammer_event'a перестает подавать признаки жизни,
|
|
|
# поэтому гасим, когда включается глушилка и запускаем заново, когда глушилка отключается.
|
|
|
# 3. Пофиксить момент с data_queue:из-за асинхронных функций и старой реализации сервака происходит так ,что
|
|
|
# прилетает пакет аларма, система это видит, хочет его отправить, начинает отправку и из-за того, что это немного
|
|
|
# долгий процесс, то успевает прилететь чистый пакет и на мастер улетает чистый пакет.
|
|
|
# 4. Добавить print, только если deub_module_flag.
|
|
|
|
|
|
|
|
|
############################################################################
|
|
|
# GPS MODULE - INACTIVE
|
|
|
############################################################################
|
|
|
# Создание планировщика
|
|
|
# scheduler = BackgroundScheduler(daemon=True)
|
|
|
# scheduler.start()
|
|
|
# @app.route('/get_gps', methods = ['POST'])
|
|
|
# @scheduler.scheduled_job(IntervalTrigger(minutes=1))
|
|
|
# def update_gps_coordinates():
|
|
|
# # data_gps = request.json
|
|
|
# result = {
|
|
|
# 'latitude': latitude,
|
|
|
# 'longitude': longitude
|
|
|
# }
|
|
|
# try:
|
|
|
# url = "http://{0}:{1}/data/gps/{2}".format(master_server_ip, master_server_port, mac_address)
|
|
|
# response = requests.post(url, json=result)
|
|
|
# if response.status_code == 200:
|
|
|
# print('gps успешно отправлен')
|
|
|
# else:
|
|
|
# print('gps не был отправлен')
|
|
|
#
|
|
|
# except Exception:
|
|
|
# print('gps не были отправлены из-за отстутствия сервера в поле видимости')
|
|
|
# return result
|
|
|
#
|
|
|
#
|
|
|
# @scheduler.scheduled_job(IntervalTrigger(seconds=10))
|
|
|
# def send_gps_to_master():
|
|
|
# try:
|
|
|
# subprocess.run(["python3", "GPS_get_coords.py"])
|
|
|
# #mac_address = get_mac_address()
|
|
|
# data_gps = update_gps_coordinates()
|
|
|
# url = "http://{0}:{1}/data/gps/{2}".format(master_server_ip, master_server_port, mac_address)
|
|
|
# response = requests.post(url, json=data_gps)
|
|
|
# if response.status_code == 200:
|
|
|
# print('gps успешно отправлен')
|
|
|
# else:
|
|
|
# print('gps не был отправлены по какой-то причине')
|
|
|
# except Exception:
|
|
|
# print('gps не были отправлены по причине отсутствия сервера в поле видимости')
|
|
|
#
|
|
|
|
|
|
|
|
|
############################################################################
|
|
|
# MODULE RIGISTR
|
|
|
############################################################################
|
|
|
def get_mac_address(interface='enp5s0'):
|
|
|
"""
|
|
|
Получить мак текущего устройства, на котором развернут модуль сервер.
|
|
|
:param interface:
|
|
|
"""
|
|
|
try:
|
|
|
result = os.popen('sudo ifconfig ' + interface).read()
|
|
|
mac_index = result.find('ether') # Индекс начала строки с MAC-адресом
|
|
|
if mac_index != -1:
|
|
|
mac_address = result[mac_index + 6:mac_index + 23]
|
|
|
return mac_address
|
|
|
else:
|
|
|
return None
|
|
|
except Exception as e:
|
|
|
print("Ошибка при получении MAC-адреса:" + str(e))
|
|
|
return None
|
|
|
|
|
|
|
|
|
def get_ip_address(interface='enp5s0'):
|
|
|
"""
|
|
|
Получить айпишник текущего устройства, на котором развернут модуль сервер.
|
|
|
:param interface:
|
|
|
"""
|
|
|
try:
|
|
|
result = os.popen('sudo ifconfig ' + interface).read()
|
|
|
ip_index = result.find('inet') # Индекс начала строки с IP-адресом
|
|
|
if ip_index != -1:
|
|
|
ip_address = result[ip_index + 5:ip_index + 19]
|
|
|
return ip_address.strip()
|
|
|
else:
|
|
|
return None
|
|
|
except Exception as e:
|
|
|
print("Ошибка при получении IP-адреса:" + str(e))
|
|
|
return None
|
|
|
|
|
|
|
|
|
def register_module():
|
|
|
"""
|
|
|
Регистрация модуля на мастер сервере.
|
|
|
"""
|
|
|
|
|
|
data = {'mac': get_mac_address(),
|
|
|
'ip': get_ip_address(),
|
|
|
'moduleType': 'freq'}
|
|
|
try:
|
|
|
url = f"http://{master_server_ip}:{master_server_port}/module/register"
|
|
|
response = requests.post(url, json=data)
|
|
|
response.raise_for_status() # Проверка успешности запроса
|
|
|
print("Модуль зарегистрирован успешно = ", data)
|
|
|
except requests.exceptions.RequestException as e:
|
|
|
flag = 1
|
|
|
print("Ошибка при регистрации модуля:" + str(e), data)
|
|
|
|
|
|
|
|
|
############################################################################
|
|
|
# SEND DATA TO MASTER
|
|
|
############################################################################
|
|
|
async def send_to_master(ModuleDataSingleV2, flag):
|
|
|
"""
|
|
|
Отправка данных на мастер по посту или через булк. По посту установлен лимит времени на отправку. В случае его
|
|
|
превышения - данные не отправлены. В случа неудачи отправки по любому из методов - данные не отправлены.
|
|
|
:param ModuleDataSingleV2: Пакет данных.
|
|
|
:param flag:
|
|
|
:return:
|
|
|
"""
|
|
|
mac_address = get_mac_address()
|
|
|
async with httpx.AsyncClient() as client:
|
|
|
try:
|
|
|
if flag == 0:
|
|
|
url = f"http://{master_server_ip}:{master_server_port}/data/single/{mac_address}"
|
|
|
else:
|
|
|
url = f"http://{master_server_ip}:{master_server_port}/data/bulk/{mac_address}"
|
|
|
response = await client.post(url, json=ModuleDataSingleV2, timeout=master_timeout)
|
|
|
if response.status_code == 200:
|
|
|
print('Данные успешно отправлены')
|
|
|
flag = 0
|
|
|
bulk_data.clear()
|
|
|
else:
|
|
|
print(response.text)
|
|
|
flag = 1
|
|
|
if len(bulk_data) > max_len_bulk: # Если лимит bulk_data превышен, то удаляем первый элемент списка
|
|
|
bulk_data.pop(0)
|
|
|
bulk_data.append(ModuleDataSingleV2)
|
|
|
print('Данные не были отправлены по какой-то причине')
|
|
|
except (httpx.RequestError, asyncio.TimeoutError) as e:
|
|
|
if len(bulk_data) > max_len_bulk: # Если лимит bulk_data превышен, то удаляем первый элемент списка
|
|
|
bulk_data.pop(0)
|
|
|
bulk_data.append(ModuleDataSingleV2)
|
|
|
flag = 1
|
|
|
print('Данные не были отправлены по причине отсутствия сервера в поле видимости')
|
|
|
|
|
|
|
|
|
############################################################################
|
|
|
# PROCESS DATA
|
|
|
############################################################################
|
|
|
async def check_alarm(amplitude: int):
|
|
|
"""
|
|
|
Проверка амплитуды на превышение границы отработки системы.
|
|
|
:param amplitude: Амплитуда.
|
|
|
:return: Превышает/не превышает.
|
|
|
"""
|
|
|
if amplitude > threshold_to_alarm:
|
|
|
return True
|
|
|
else:
|
|
|
return False
|
|
|
|
|
|
|
|
|
async def agregate_data(data_to_agregate: list):
|
|
|
"""
|
|
|
Сбор пакета для отправки на мастер сервер.
|
|
|
:param data_to_agregate: Список из частотных пакетов. Длина списка = количесту обнаруживаемых частот. Может
|
|
|
содержать None, если частота ничего не присылает на модуль сервер.
|
|
|
:return: Пакет данных для отправки на мастер.
|
|
|
"""
|
|
|
|
|
|
data = []
|
|
|
|
|
|
if any(item is not None for item in data_to_agregate):
|
|
|
for item in data_to_agregate:
|
|
|
if item is not None:
|
|
|
item['freq']=int(item['freq'])
|
|
|
data.append(item)
|
|
|
|
|
|
now = datetime.utcnow() - timedelta(seconds=2)
|
|
|
now = now.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
data = {
|
|
|
"registeredAt": now,
|
|
|
"data": data
|
|
|
}
|
|
|
|
|
|
for i in range(len(freqs)):
|
|
|
data_queue[i] = None
|
|
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
async def sending_data():
|
|
|
#TODO: Надо по-хорошему нормально эту функцию переписать
|
|
|
"""
|
|
|
Отправка пакета данных на мастер сервер раз в некоторое время в определенном формате. Время отправки зависит
|
|
|
от текущего статуса тревоги (аларм/не аларм).
|
|
|
"""
|
|
|
|
|
|
global alarm
|
|
|
global jammer_event
|
|
|
|
|
|
while True:
|
|
|
print('while true!')
|
|
|
ModuleDataSingleV2 = await agregate_data(deepcopy(data_queue))
|
|
|
if send_to_master_flag:
|
|
|
print(f'На Мастер будет отправлена следующая информация: {ModuleDataSingleV2}')
|
|
|
await send_to_master(ModuleDataSingleV2, flag)
|
|
|
|
|
|
# Если перед отправкой на мастер все было чисто, то ждем 60 сек.
|
|
|
# Если во время этих 60 сек. пришел пакет с алармом, то рассматриваем ситуации:
|
|
|
if not alarm:
|
|
|
for i in range(passive_interval_to_send, 0, -1):
|
|
|
print('ТАЙМЕР ', i)
|
|
|
await asyncio.sleep(1)
|
|
|
if alarm:
|
|
|
break
|
|
|
|
|
|
# Если стоит флаг отправить данные на джеммер и при этом еще не был получен ивент на глушилку, то
|
|
|
# отправляем на джеммер данные.
|
|
|
elif alarm and send_to_jammer_flag and not jammer_event:
|
|
|
if await send_jam_server_alarm():
|
|
|
print('Отправили на сервис подавления и все дошло успешно')
|
|
|
else:
|
|
|
print('Не смогли отправить на сервис подавления')
|
|
|
|
|
|
# Сюда почему-то не заходит и вообще функция не подает признаков жизни после запуска подваителя((
|
|
|
if alarm and jammer_event:
|
|
|
print('ПОДАВИТЕЛЬ РАБОТАЕТ РАЗБЕГАЙСЯ ААААААААААААААААААА')
|
|
|
|
|
|
# В случае аларма ждем секунду перед новой отправкой данных.
|
|
|
if alarm:
|
|
|
await asyncio.sleep(active_interval_to_send)
|
|
|
|
|
|
|
|
|
@app.post('/waterfall')
|
|
|
async def waterfall(data: dict):
|
|
|
print('Received data: ', data)
|
|
|
|
|
|
|
|
|
@app.post('/process_data')
|
|
|
async def process_data(data: dict):
|
|
|
"""
|
|
|
Прием данных со скриптов детекции в формате data = {"freq": freq,
|
|
|
"amplitude": amplitude
|
|
|
}
|
|
|
где freq - строка, amplitude - int и их первичная обработка.
|
|
|
:param data: словарь с двумя ключами и значениями.
|
|
|
"""
|
|
|
|
|
|
global alarm
|
|
|
|
|
|
print('Received data: ', data)
|
|
|
data_dict = deepcopy(data)
|
|
|
|
|
|
# Агрегируем N пакетов данных от частот в один общий список, он используется в функции agregate data.
|
|
|
# Каждая позиция списка фиксируется за отдельной частотой.
|
|
|
freq = data_dict['freq']
|
|
|
for i in range(len(freqs)):
|
|
|
if freq == freqs[i]:
|
|
|
#Так делаем потому, что сервак является центром принятия решений по триггеру.
|
|
|
trigger = await check_alarm(data_dict['amplitude'])
|
|
|
data_dict.update({'triggered': trigger})
|
|
|
|
|
|
data_queue[i] = deepcopy(data_dict)
|
|
|
data_dict.clear()
|
|
|
|
|
|
# Если прилетел триггер и глушилка не включена, то запускаем/обновляем счетчик чистых пакетов на этой
|
|
|
# частоте.
|
|
|
if trigger and not jammer_event:
|
|
|
freqs_alarm[freq] = num_of_clear_packs
|
|
|
print(f'freqs_alarm выглядит следующим обазом: {freqs_alarm}')
|
|
|
|
|
|
#Если прилетел триггер и модуль еще не заалармлен, то алармим.
|
|
|
if trigger and not alarm:
|
|
|
print('Прилет триггерa со сканнера. Работаем, ребята!')
|
|
|
alarm = True
|
|
|
|
|
|
# Если прилетел триггер и модуль заалармлен, но при этом глушилка не работает и счетчик чистых пакетов
|
|
|
# данной частоты не равен нулю, то уменьшаем его. А когда в словаре счетчиков все нули, то убираем alarm.
|
|
|
elif not trigger and alarm and not jammer_event and freqs_alarm[freq] != 0:
|
|
|
freqs_alarm[freq] -= 1
|
|
|
print(f'Чистый пакет. Уменьшаем в выбранной частоте: {freqs_alarm}')
|
|
|
|
|
|
if all(value == 0 for value in freqs_alarm.values()):
|
|
|
alarm = False
|
|
|
print(f'Прилетело {num_of_clear_packs}. Отключаем аларм и freqs_alarm выглядит так: {freqs_alarm}')
|
|
|
|
|
|
else:
|
|
|
continue
|
|
|
|
|
|
print('После получения данных data_queue выглядит следующим образом: ', data_queue)
|
|
|
|
|
|
|
|
|
############################################################################
|
|
|
# JAMMER
|
|
|
############################################################################
|
|
|
async def jammer_active():
|
|
|
global p
|
|
|
"""
|
|
|
Включение подавителя.
|
|
|
Отменяем таску на отправку данных на мастер. Зануляем словарь чистых пакетов и объявляем о прилете ивента с сервиса
|
|
|
подавления.
|
|
|
"""
|
|
|
|
|
|
global jammer_event
|
|
|
global freqs_alarm
|
|
|
global sending_data_task
|
|
|
|
|
|
if sending_data_task is not None:
|
|
|
sending_data_task.cancel()
|
|
|
p = 0
|
|
|
print(p)
|
|
|
|
|
|
freqs_alarm = {freq: 0 for freq in freqs}
|
|
|
jammer_event = True
|
|
|
|
|
|
print('АКТИВИРУЕМ ПОДАВИТЕЛЬ ААААААААААААААААААААААААААААААААААААААААААААААА!!!!')
|
|
|
print('-' * 20)
|
|
|
print('Статус по переменным:')
|
|
|
print(f'freqs_alarm: {freqs_alarm}')
|
|
|
print(f'jammer_event: {jammer_event}')
|
|
|
print(f'alarm: {alarm}')
|
|
|
print('-' * 20)
|
|
|
|
|
|
|
|
|
async def jammer_deactive():
|
|
|
global p
|
|
|
"""
|
|
|
Отключение подавителя.
|
|
|
Отрубаем аларм на модуле, отрубаем ивент сервера подавителей и запускаем таску отправки данных на мастер.
|
|
|
:return:
|
|
|
"""
|
|
|
|
|
|
global jammer_event
|
|
|
global alarm
|
|
|
global sending_data_task
|
|
|
alarm = False
|
|
|
jammer_event = False
|
|
|
if p == 0:
|
|
|
sending_data_task = asyncio.create_task(sending_data())
|
|
|
p = 1
|
|
|
#print(p)
|
|
|
|
|
|
|
|
|
print('ОТКЛЮАЕМ ПОДАВИТЕЛЬ ААААААААААААААААААААААААААААААААААААААААААААААААА!!!!')
|
|
|
print('-' * 20)
|
|
|
print('Статус по переменным:')
|
|
|
print(f'freqs_alarm: {freqs_alarm}')
|
|
|
print(f'jammer_event: {jammer_event}')
|
|
|
print(f'alarm: {alarm}')
|
|
|
print('-' * 20)
|
|
|
|
|
|
|
|
|
async def send_jam_server_alarm():
|
|
|
"""
|
|
|
Отправка алармовского пакета на сервер подавления по вебсокету. На отправку дается jammer_timeout секунд.
|
|
|
При неудаче - данные не отправлены.
|
|
|
"""
|
|
|
|
|
|
global jam_server_connect
|
|
|
|
|
|
msg = {'type': 'freq_alarm',
|
|
|
'data': True}
|
|
|
|
|
|
if jam_server_connect:
|
|
|
try:
|
|
|
await jam_server_connect.send(json.dumps(msg))
|
|
|
await asyncio.wait_for(jam_server_connect.recv())
|
|
|
return True
|
|
|
except (asyncio.TimeoutError, websockets.exceptions.WebSocketException) as e:
|
|
|
print(f"WebSocket error or timeout: {e}")
|
|
|
return False
|
|
|
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 jammer_event:
|
|
|
await jammer_deactive()
|
|
|
|
|
|
|
|
|
|
|
|
@app.on_event("startup")
|
|
|
async def startup_event():
|
|
|
"""
|
|
|
Запускаем параллельно задачи jam_server и sending_data.
|
|
|
"""
|
|
|
|
|
|
global sending_data_task
|
|
|
asyncio.create_task(jam_server())
|
|
|
sending_data_task = asyncio.create_task(sending_data())
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
# update_gps_coordinates()
|
|
|
register_module() # Регистрация модуля на сервере
|
|
|
uvicorn.run(app, host=lochost, port=int(locport)) |