Files
god_eye_android/godeye_server.py
2025-10-06 09:40:51 +09:00

351 lines
13 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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}")