main commit
This commit is contained in:
350
godeye_server.py
Normal file
350
godeye_server.py
Normal file
@@ -0,0 +1,350 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Простой веб-сервер для панели оператора GodEye
|
||||
Обслуживает WebRTC интерфейс и Socket.IO соединения
|
||||
"""
|
||||
|
||||
import socketio
|
||||
import eventlet
|
||||
import eventlet.wsgi
|
||||
from flask import Flask, send_file
|
||||
import json
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
||||
# Создаем Flask приложение
|
||||
app = Flask(__name__)
|
||||
|
||||
# Создаем Socket.IO сервер
|
||||
sio = socketio.Server(cors_allowed_origins="*")
|
||||
app.wsgi_app = socketio.WSGIApp(sio, app.wsgi_app)
|
||||
|
||||
# Хранилище подключенных устройств и операторов
|
||||
connected_devices = {}
|
||||
connected_operators = {}
|
||||
active_sessions = {}
|
||||
|
||||
@app.route('/')
|
||||
def serve_interface():
|
||||
"""Обслуживает панель оператора"""
|
||||
return send_file('operator-interface.html')
|
||||
|
||||
@sio.event
|
||||
def connect(sid, environ):
|
||||
"""Обработка подключения клиента"""
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] 🔗 Клиент подключился: {sid}")
|
||||
|
||||
@sio.event
|
||||
def disconnect(sid):
|
||||
"""Обработка отключения клиента"""
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] ❌ Клиент отключился: {sid}")
|
||||
|
||||
# Удаляем из устройств
|
||||
if sid in connected_devices:
|
||||
device_data = connected_devices[sid]
|
||||
del connected_devices[sid]
|
||||
sio.emit('device_disconnected', {
|
||||
'deviceId': device_data['deviceId'],
|
||||
'deviceName': device_data['deviceName']
|
||||
})
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] 📱 Устройство отключилось: {device_data['deviceName']}")
|
||||
|
||||
# Удаляем из операторов
|
||||
if sid in connected_operators:
|
||||
del connected_operators[sid]
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] 👨💼 Оператор отключился")
|
||||
|
||||
@sio.event
|
||||
def device_register(sid, data):
|
||||
"""Регистрация Android устройства"""
|
||||
device_id = data.get('deviceId')
|
||||
device_name = data.get('deviceName', 'Unknown Device')
|
||||
|
||||
connected_devices[sid] = {
|
||||
'deviceId': device_id,
|
||||
'deviceName': device_name,
|
||||
'connectedAt': datetime.now().isoformat()
|
||||
}
|
||||
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] 📱 Устройство зарегистрировано: {device_name} ({device_id})")
|
||||
|
||||
# Уведомляем всех операторов о новом устройстве
|
||||
sio.emit('device_connected', {
|
||||
'deviceId': device_id,
|
||||
'deviceName': device_name
|
||||
})
|
||||
|
||||
# Отправляем подтверждение устройству
|
||||
sio.emit('registration_confirmed', {
|
||||
'status': 'success',
|
||||
'message': 'Устройство успешно зарегистрировано'
|
||||
}, room=sid)
|
||||
|
||||
@sio.event
|
||||
def operator_register(sid, data):
|
||||
"""Регистрация оператора"""
|
||||
operator_id = data.get('operatorId', f'operator_{uuid.uuid4().hex[:8]}')
|
||||
|
||||
connected_operators[sid] = {
|
||||
'operatorId': operator_id,
|
||||
'connectedAt': datetime.now().isoformat()
|
||||
}
|
||||
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] 👨💼 Оператор зарегистрирован: {operator_id}")
|
||||
|
||||
# Отправляем список подключенных устройств
|
||||
devices_list = []
|
||||
for device_sid, device_data in connected_devices.items():
|
||||
devices_list.append({
|
||||
'deviceId': device_data['deviceId'],
|
||||
'deviceName': device_data['deviceName']
|
||||
})
|
||||
|
||||
sio.emit('devices_list', {'devices': devices_list}, room=sid)
|
||||
|
||||
@sio.event
|
||||
def request_camera(sid, data):
|
||||
"""Запрос камеры от оператора"""
|
||||
device_id = data.get('deviceId')
|
||||
operator_id = data.get('operatorId')
|
||||
camera_type = data.get('cameraType', 'back') # Убеждаемся что есть значение по умолчанию
|
||||
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] 📹 Запрос камеры от {operator_id} для устройства {device_id}, тип камеры: {camera_type}")
|
||||
|
||||
# Находим устройство по ID
|
||||
target_device_sid = None
|
||||
for device_sid, device_data in connected_devices.items():
|
||||
if device_data['deviceId'] == device_id:
|
||||
target_device_sid = device_sid
|
||||
break
|
||||
|
||||
if target_device_sid:
|
||||
session_id = str(uuid.uuid4())
|
||||
|
||||
# Сохраняем сессию с правильными данными
|
||||
active_sessions[session_id] = {
|
||||
'deviceSid': target_device_sid,
|
||||
'operatorSid': sid,
|
||||
'deviceId': device_id,
|
||||
'operatorId': operator_id,
|
||||
'cameraType': camera_type, # Явно сохраняем тип камеры
|
||||
'status': 'pending', # Добавляем статус
|
||||
'startedAt': datetime.now().isoformat()
|
||||
}
|
||||
|
||||
# Отправляем запрос устройству с полными данными
|
||||
sio.emit('camera_request', {
|
||||
'sessionId': session_id,
|
||||
'operatorId': operator_id,
|
||||
'cameraType': camera_type,
|
||||
'deviceId': device_id, # Добавляем deviceId
|
||||
'message': f'Оператор {operator_id} запрашивает доступ к камере'
|
||||
}, room=target_device_sid)
|
||||
|
||||
# Уведомляем оператора о создании сессии
|
||||
sio.emit('session_created', {
|
||||
'sessionId': session_id,
|
||||
'deviceId': device_id,
|
||||
'cameraType': camera_type,
|
||||
'status': 'pending'
|
||||
}, room=sid)
|
||||
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] 📤 Запрос отправлен устройству, сессия: {session_id}, камера: {camera_type}")
|
||||
else:
|
||||
sio.emit('error', {
|
||||
'message': f'Устройство {device_id} не найдено'
|
||||
}, room=sid)
|
||||
|
||||
@sio.event
|
||||
def camera_approved(sid, data):
|
||||
"""Подтверждение доступа к камере от устройства"""
|
||||
session_id = data.get('sessionId')
|
||||
|
||||
if session_id in active_sessions:
|
||||
session = active_sessions[session_id]
|
||||
operator_sid = session['operatorSid']
|
||||
|
||||
# Обновляем статус сессии
|
||||
active_sessions[session_id]['status'] = 'approved'
|
||||
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] ✅ Доступ к камере подтвержден для сессии: {session_id}")
|
||||
|
||||
# Уведомляем оператора с полными данными
|
||||
sio.emit('camera_approved', {
|
||||
'sessionId': session_id,
|
||||
'deviceId': session['deviceId'],
|
||||
'cameraType': session['cameraType'],
|
||||
'status': 'approved'
|
||||
}, room=operator_sid)
|
||||
|
||||
@sio.event
|
||||
def camera_rejected(sid, data):
|
||||
"""Отклонение доступа к камере от устройства"""
|
||||
session_id = data.get('sessionId')
|
||||
reason = data.get('reason', 'Отклонено пользователем')
|
||||
|
||||
if session_id in active_sessions:
|
||||
session = active_sessions[session_id]
|
||||
operator_sid = session['operatorSid']
|
||||
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] ❌ Доступ к камере отклонен для сессии: {session_id}, причина: {reason}")
|
||||
|
||||
# Уведомляем оператора об отклонении
|
||||
sio.emit('camera_rejected', {
|
||||
'sessionId': session_id,
|
||||
'deviceId': session['deviceId'],
|
||||
'reason': reason
|
||||
}, room=operator_sid)
|
||||
|
||||
# Удаляем сессию, так как она отклонена
|
||||
del active_sessions[session_id]
|
||||
|
||||
@sio.event
|
||||
def camera_started(sid, data):
|
||||
"""Уведомление о запуске камеры"""
|
||||
session_id = data.get('sessionId')
|
||||
|
||||
if session_id in active_sessions:
|
||||
session = active_sessions[session_id]
|
||||
operator_sid = session['operatorSid']
|
||||
|
||||
# Обновляем статус сессии на активный
|
||||
active_sessions[session_id]['status'] = 'active'
|
||||
active_sessions[session_id]['actualStartedAt'] = datetime.now().isoformat()
|
||||
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] 📹 Камера запущена для сессии: {session_id}")
|
||||
|
||||
# Уведомляем оператора с обновленным статусом
|
||||
sio.emit('camera_started', {
|
||||
'sessionId': session_id,
|
||||
'deviceId': session['deviceId'],
|
||||
'cameraType': session['cameraType'],
|
||||
'status': 'active'
|
||||
}, room=operator_sid)
|
||||
|
||||
# Также отправляем обновление статуса сессии
|
||||
sio.emit('session_status_update', {
|
||||
'sessionId': session_id,
|
||||
'status': 'active',
|
||||
'cameraType': session['cameraType']
|
||||
}, room=operator_sid)
|
||||
|
||||
@sio.event
|
||||
def webrtc_offer(sid, data):
|
||||
"""Пересылка WebRTC offer от устройства к оператору"""
|
||||
session_id = data.get('sessionId')
|
||||
offer = data.get('offer')
|
||||
|
||||
if session_id in active_sessions:
|
||||
session = active_sessions[session_id]
|
||||
operator_sid = session['operatorSid']
|
||||
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] 📡 Пересылка WebRTC offer для сессии: {session_id}")
|
||||
|
||||
# Пересылаем offer оператору
|
||||
sio.emit('webrtc_offer', {
|
||||
'sessionId': session_id,
|
||||
'offer': offer
|
||||
}, room=operator_sid)
|
||||
|
||||
@sio.event
|
||||
def webrtc_answer(sid, data):
|
||||
"""Пересылка WebRTC answer от оператора к устройству"""
|
||||
session_id = data.get('sessionId')
|
||||
answer = data.get('answer')
|
||||
|
||||
if session_id in active_sessions:
|
||||
session = active_sessions[session_id]
|
||||
device_sid = session['deviceSid']
|
||||
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] 📡 Пересылка WebRTC answer для сессии: {session_id}")
|
||||
|
||||
# Пересылаем answer устройству
|
||||
sio.emit('webrtc_answer', {
|
||||
'sessionId': session_id,
|
||||
'answer': answer
|
||||
}, room=device_sid)
|
||||
|
||||
@sio.event
|
||||
def webrtc_ice_candidate(sid, data):
|
||||
"""Пересылка ICE candidates между устройством и оператором"""
|
||||
session_id = data.get('sessionId')
|
||||
candidate = data.get('candidate')
|
||||
|
||||
if session_id in active_sessions:
|
||||
session = active_sessions[session_id]
|
||||
|
||||
# Определяем куда пересылать (от устройства к оператору или наоборот)
|
||||
if sid == session['deviceSid']:
|
||||
# От устройства к оператору
|
||||
target_sid = session['operatorSid']
|
||||
else:
|
||||
# От оператора к устройству
|
||||
target_sid = session['deviceSid']
|
||||
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] 🧊 Пересылка ICE candidate для сессии: {session_id}")
|
||||
|
||||
sio.emit('webrtc_ice_candidate', {
|
||||
'sessionId': session_id,
|
||||
'candidate': candidate
|
||||
}, room=target_sid)
|
||||
|
||||
@sio.event
|
||||
def switch_camera(sid, data):
|
||||
"""Переключение камеры"""
|
||||
session_id = data.get('sessionId')
|
||||
|
||||
if session_id in active_sessions:
|
||||
session = active_sessions[session_id]
|
||||
device_sid = session['deviceSid']
|
||||
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] 🔄 Переключение камеры для сессии: {session_id}")
|
||||
|
||||
# Отправляем команду устройству
|
||||
sio.emit('switch_camera', {
|
||||
'sessionId': session_id
|
||||
}, room=device_sid)
|
||||
|
||||
@sio.event
|
||||
def stop_camera(sid, data):
|
||||
"""Остановка камеры"""
|
||||
session_id = data.get('sessionId')
|
||||
|
||||
if session_id in active_sessions:
|
||||
session = active_sessions[session_id]
|
||||
device_sid = session['deviceSid']
|
||||
operator_sid = session['operatorSid']
|
||||
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] 🛑 Остановка камеры для сессии: {session_id}")
|
||||
|
||||
# Отправляем команду устройству
|
||||
sio.emit('stop_camera', {
|
||||
'sessionId': session_id
|
||||
}, room=device_sid)
|
||||
|
||||
# Уведомляем оператора
|
||||
sio.emit('camera_stopped', {
|
||||
'sessionId': session_id
|
||||
}, room=operator_sid)
|
||||
|
||||
# Удаляем сессию
|
||||
del active_sessions[session_id]
|
||||
|
||||
if __name__ == '__main__':
|
||||
# ИСПРАВЛЕНО: Используем localhost для локального тестирования
|
||||
host = '0.0.0.0' # Слушаем на всех интерфейсах
|
||||
port = 3001
|
||||
|
||||
print("🚀 Запуск GodEye сервера...")
|
||||
print(f"🌐 Веб-интерфейс: http://localhost:{port}")
|
||||
print(f"🔌 Socket.IO endpoint: http://localhost:{port}/socket.io/")
|
||||
print("📱 Настройте Android приложение на адрес вашего компьютера")
|
||||
print("-" * 50)
|
||||
|
||||
try:
|
||||
eventlet.wsgi.server(eventlet.listen((host, port)), app)
|
||||
except KeyboardInterrupt:
|
||||
print("\n🛑 Сервер остановлен")
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка сервера: {e}")
|
||||
Reference in New Issue
Block a user