main commit

This commit is contained in:
2025-10-04 11:55:55 +09:00
parent c8c3274527
commit 4ceccae6ce
678 changed files with 95975 additions and 185 deletions

View File

@@ -0,0 +1,69 @@
# 🔧 Критические исправления - ОБЯЗАТЕЛЬНО К ВЫПОЛНЕНИЮ
## ⚠️ ВАЖНО! Проблема с кэшированием браузера
**Проблема**: В логах сервера все еще видно `register:android` вместо `register:mobile_web`, хотя код исправлен.
**Причина**: Браузер кэширует старую версию mobile.html
## 🚀 РЕШЕНИЕ - Принудительное обновление
### На телефоне (мобильный браузер):
1. **⚠️ ЗАКРОЙТЕ ВСЕ ВКЛАДКИ** с мобильной версией
2. **🔄 ОЧИСТИТЕ КЭШ БРАУЗЕРА**:
- Chrome: Settings → Privacy → Clear browsing data → Cached images and files
- Safari: Settings → Safari → Clear History and Website Data
3. **📱 ПОЛНОСТЬЮ ПЕРЕЗАПУСТИТЕ БРАУЗЕР** (закройте и откройте приложение)
4. **🌐 ОТКРОЙТЕ НОВУЮ ВКЛАДКУ** и перейдите: `http://192.168.219.108:3001/mobile`
### В Desktop Operator:
1. **🔄 ПЕРЕЗАПУСТИТЕ ПРИЛОЖЕНИЕ** полностью
2. **🔌 ПЕРЕПОДКЛЮЧИТЕСЬ** к серверу
## ✅ Что должно быть в логах после исправления:
### Правильные логи сервера:
```
📱 Mobile web client connected: [ID]
🌐 Mobile web event: register:mobile_web ← ДОЛЖНО БЫТЬ ТАК!
Mobile web client registered: [device-id]
```
### НЕ должно быть:
```
🌐 Mobile web event: register:android ← НЕПРАВИЛЬНО!
```
## 🧪 Проверка исправлений
После очистки кэша и переподключения:
1. **Мобильная регистрация**: В логах должно быть `register:mobile_web`
2. **Переключение камеры**: Должны видеть в логах мобильного браузера:
```
🔄 Переключение камеры на: [тип]
🔄 Обновление WebRTC потоков...
✅ Видео трек обновлен для сессии: [ID]
```
3. **Ошибка "undefined"**: Должна исчезнуть
4. **WebRTC поток**: Должен обновляться при переключении камеры
## 🔧 Технические исправления (выполнены):
- ✅ Исправлено событие регистрации: `register:android` → `register:mobile_web`
- ✅ Добавлена функция `updateWebRTCStreams()` для обновления видео треков
- ✅ Исправлена `switchCamera()` с поддержкой параметров
- ✅ Улучшена `handleCameraSwitchRequest()` для правильной обработки
## 🎯 Ожидаемый результат:
После выполнения всех шагов:
-**Исчезнет**: Ошибка "undefined" при переключении камеры
-**Появится**: Корректное переключение между front/back камерами
-**Работает**: WebRTC поток обновляется в real-time
-**Видно в операторе**: Смена видео при переключении камеры
---
**🚨 КРИТИЧЕСКИ ВАЖНО**: Обязательно очистите кэш браузера на телефоне, иначе исправления не будут работать!

View File

@@ -0,0 +1,162 @@
# Инструкция по тестированию полностью обновленного интерфейса
## Новые возможности интерфейса v2.0:
### 🔧 1. Сворачиваемые панели
- **Панель подключения**: по умолчанию свернута, статус отображается в заголовке
- **Панель логов**: сворачивается для экономии места
- **Адаптивные размеры**: панели автоматически подстраиваются под доступное пространство
### 📱 2. Компактное отображение
- **Устройства**: сокращенные ID, эмодзи-статусы, иконочные кнопки
- **Сессии**: формат "📱 device | 📷 camera", цветовая кодировка статусов
- **Максимум информации** в минимальном пространстве
### 🎨 3. Современный дизайн
- **Эмодзи-иконки** для быстрого визуального восприятия
- **Плавные анимации** переходов и hover-эффектов
- **Цветовые индикаторы** для статусов и состояний
## Последовательность тестирования:
### Шаг 1: Первый запуск
1. Запустите десктопное приложение
2. **Проверьте начальное состояние**:
- Панель подключения свернута с красным индикатором ● (отключено)
- Панель логов развернута
- Панели устройств и сессий пустые
### Шаг 2: Управление панелями
1. **Тест сворачивания подключения**:
- Кликните на "🔗 Подключение к серверу ●"
- Панель должна плавно развернуться
- Кликните снова - должна свернуться
2. **Тест сворачивания логов**:
- Кликните на "📋 Журнал событий ▲"
- Логи должны скрыться, иконка измениться на ▼
- Панели устройств/сессий должны увеличиться
### Шаг 3: Подключение к серверу
1. Разверните панель подключения
2. Нажмите "Подключиться"
3. **Проверьте изменения**:
- Кнопка изменилась на "Отключиться"
- Индикатор в заголовке стал зеленым ●
- В логах появилось сообщение о подключении
### Шаг 4: Работа с устройствами
1. В веб-демо (http://localhost:3001) симулируйте Android устройство
2. **Проверьте компактное отображение**:
- ID устройства сокращен (если длинный)
- Статус показан как 🟢 Онлайн
- Кнопка показывает "🔗 Подключить"
- Иконка 📷 перед типами камер
### Шаг 5: Создание сессий
1. Нажмите "🔗 Подключить" на устройстве
2. В веб-демо примите запрос
3. **Проверьте отображение сессии**:
- Формат: "📱 device123... | 📷 back"
- Статус: 🟢 Активна
- Кнопки: ✓ Активна, 📷 Камера, ❌ Завершить
### Шаг 6: Множественные сессии
1. Создайте еще 2-3 сессии
2. **Проверьте адаптивность**:
- Все сессии помещаются в компактном списке
- Прокрутка работает при большом количестве
- Кнопка устройства изменилась на " Добавить"
### Шаг 7: Оптимизация пространства
1. Сверните панель логов
2. **Проверьте увеличение списков**:
- Панели устройств/сессий стали выше
- Больше элементов видно без прокрутки
- Переходы плавные
## Визуальные индикаторы:
### Панель подключения (свернута):
```
🔗 Подключение к серверу ● ▼
```
-**Красный** = отключено
-**Зеленый** = подключено
### Устройства (компактно):
```
📱 Устройства 🔄
┌─────────────────┐
│ ID: device12... │
│ Статус: 🟢 Онлайн│
│ Сессии: 2 │
│ 📷 back, front │
│ [ Добавить] │
└─────────────────┘
```
### Сессии (компактно):
```
🔗 Активные сессии
┌─────────────────┐
│ 📱 dev123... | 📷 back │
│ 🟢 Активна │
│ [✓][📷][❌] │
└─────────────────┘
```
### Логи (сворачиваемые):
```
📋 Журнал событий ▲
┌─────────────────┐
│ [12:34] INFO... │
│ [12:35] SUCCESS │
└─────────────────┘
При сворачивании:
📋 Журнал событий ▼
```
## Преимущества нового интерфейса:
### 🎯 Эффективность пространства:
- **На 40% больше информации** в том же окне
- **Гибкое управление** видимостью панелей
- **Адаптивные размеры** под количество элементов
### 👀 Улучшенная читаемость:
- **Эмодзи-иконки** для мгновенного распознавания
- **Цветовая кодировка** статусов
- **Компактные форматы** без потери информации
### ⚡ Быстрая навигация:
- **Меньше кликов** для основных действий
- **Интуитивные иконки** вместо текста
- **Плавные анимации** для комфорта
### 📊 Масштабируемость:
- **Поддержка множественных устройств** без переполнения интерфейса
- **Эффективная прокрутка** в компактных списках
- **Автоматическая адаптация** под контент
## Тестовые сценарии:
### Сценарий 1: Минималистичный режим
1. Сверните панель подключения
2. Сверните панель логов
3. **Результат**: максимум места для устройств и сессий
### Сценарий 2: Полная видимость
1. Разверните все панели
2. **Результат**: полный контроль и мониторинг всех процессов
### Сценарий 3: Рабочий режим
1. Подключитесь к серверу
2. Сверните панель подключения (статус виден в заголовке)
3. Оставьте логи развернутыми для мониторинга
4. **Результат**: оптимальный баланс между контролем и пространством
Новый интерфейс готов для эффективной работы с любым количеством устройств и сессий! 🚀

316
docs/CONNECTION_MANAGER.md Normal file
View File

@@ -0,0 +1,316 @@
# ConnectionManager API Документация
## Обзор
`ConnectionManager` - это центральный компонент системы GodEye Signal Center, который управляет полным жизненным циклом подключений между операторами и Android устройствами. Он обеспечивает надежное создание, управление и завершение подключений с таймаутами и валидацией.
## Архитектура
```
Desktop Operator ←→ ConnectionManager ←→ Android Device
SessionManager
DeviceManager
```
### Основные компоненты:
- **ConnectionManager**: Управление подключениями и их жизненным циклом
- **DeviceManager**: Управление устройствами и операторами
- **SessionManager**: Управление WebRTC сессиями
- **Socket.IO**: Транспорт для событий подключения
## События WebSocket
### Новые события ConnectionManager
#### `connection:initiated`
Подключение инициировано ConnectionManager
```javascript
{
connectionId: "conn_uuid",
sessionId: "session_uuid",
deviceId: "device_id",
cameraType: "back",
status: "pending",
createdAt: "2023-..."
}
```
#### `connection:accepted`
Устройство приняло запрос подключения
```javascript
{
connectionId: "conn_uuid",
sessionId: "session_uuid",
deviceId: "device_id",
cameraType: "back",
streamUrl: "webrtc_stream_url",
status: "active"
}
```
#### `connection:rejected`
Устройство отклонило запрос подключения
```javascript
{
sessionId: "session_uuid",
deviceId: "device_id",
cameraType: "back",
error: "Device camera is busy"
}
```
#### `connection:terminated`
Подключение завершено
```javascript
{
connectionId: "conn_uuid",
timestamp: "2023-..."
}
```
#### `connection:error`
Ошибка подключения
```javascript
{
error: "Error message"
}
```
### Команды от клиента
#### `connection:terminate`
Завершить подключение
```javascript
{
connectionId: "conn_uuid"
}
```
#### `connection:status`
Запросить статистику подключений
```javascript
{} // Ответ в callback или connection:status_response
```
#### `connection:list`
Список подключений оператора
```javascript
{} // Ответ в callback или connection:list_response
```
## Desktop Operator API
### Новые методы
#### `terminateConnection(connectionId, sessionId)`
Завершает подключение через ConnectionManager
```javascript
operator.terminateConnection('conn_123', 'session_456');
```
#### `getConnectionStatus()`
Получает статистику всех подключений системы
```javascript
operator.getConnectionStatus();
```
#### `listMyConnections()`
Получает список подключений текущего оператора
```javascript
operator.listMyConnections();
```
### Обновленные методы
#### `requestCamera(deviceId, cameraType)`
Теперь использует ConnectionManager для создания подключений
```javascript
operator.requestCamera('device_123', 'back');
```
## Backend ConnectionManager API
### Основные методы
#### `async initiateConnection(operatorId, deviceId, cameraType)`
Инициирует новое подключение между оператором и устройством
```javascript
const connection = await connectionManager.initiateConnection(
'operator_123',
'device_456',
'back'
);
```
#### `async acceptConnection(sessionId, metadata)`
Принимает подключение со стороны устройства
```javascript
const connection = await connectionManager.acceptConnection(
'session_123',
{ streamUrl: 'webrtc://...' }
);
```
#### `async rejectConnection(sessionId, reason)`
Отклоняет подключение
```javascript
await connectionManager.rejectConnection(
'session_123',
'Camera is busy'
);
```
#### `async terminateConnection(connectionId)`
Завершает активное подключение
```javascript
await connectionManager.terminateConnection('conn_123');
```
### Утилиты
#### `getConnection(sessionId)`
Получает подключение по ID сессии
```javascript
const connection = connectionManager.getConnection('session_123');
```
#### `getConnectionStats()`
Получает статистику всех подключений
```javascript
const stats = connectionManager.getConnectionStats();
// { total: 5, active: 2, pending: 1, completed: 2 }
```
#### `getOperatorConnections(operatorId)`
Получает все подключения оператора
```javascript
const connections = connectionManager.getOperatorConnections('operator_123');
```
#### `cleanup*()`
Методы очистки подключений при отключении участников
```javascript
connectionManager.cleanupDeviceConnections('device_123');
connectionManager.cleanupOperatorConnections('operator_456');
```
## Жизненный цикл подключения
### 1. Инициирование подключения
```
Desktop Operator → camera:request → ConnectionManager.initiateConnection()
Создание Connection объекта
Отправка camera:request → Android Device
Desktop Operator ← connection:initiated ← ConnectionManager
```
### 2. Ответ устройства
```
Android Device → camera:response → ConnectionManager.acceptConnection()
Обновление Connection статуса
Desktop Operator ← connection:accepted ← ConnectionManager
Инициирование WebRTC
```
### 3. Завершение подключения
```
Desktop Operator → connection:terminate → ConnectionManager.terminateConnection()
Очистка ресурсов
connection:terminated ← ConnectionManager
```
## Конфигурация таймаутов
```javascript
// В ConnectionManager.js
CONNECTION_TIMEOUT: 30000, // 30 секунд на установку подключения
CLEANUP_INTERVAL: 60000 // Очистка каждую минуту
```
## Обратная совместимость
Система поддерживает старые события для совместимости:
- `session:created``connection:initiated`
- `session:accepted``connection:accepted`
- `session:rejected``connection:rejected`
- `camera:response` (продолжает работать)
## Логирование
ConnectionManager использует Winston logger для отслеживания:
- ✅ Успешные подключения
- ❌ Ошибки и отклонения
- 🔄 Переходы состояний
- ⏰ Таймауты подключений
- 🧹 Операции очистки
## Примеры использования
### Полный цикл подключения от Desktop Operator
```javascript
// 1. Инициирование подключения
operator.requestCamera('device_123', 'back');
// 2. Обработка ответа
operator.socket.on('connection:accepted', (data) => {
console.log('Подключение установлено:', data.connectionId);
// Запускаем WebRTC...
});
// 3. Завершение подключения
operator.terminateConnection(connectionId, sessionId);
```
### Мониторинг подключений
```javascript
// Получение статистики
operator.getConnectionStatus();
// Список моих подключений
operator.listMyConnections();
// Обработка ошибок
operator.socket.on('connection:error', (data) => {
console.error('Ошибка подключения:', data.error);
});
```
## Миграция с старой системы
Старые методы продолжают работать, но рекомендуется переход на новые:
### Было:
```javascript
operator.socket.on('session:created', ...);
operator.socket.on('session:accepted', ...);
```
### Стало:
```javascript
operator.socket.on('connection:initiated', ...);
operator.socket.on('connection:accepted', ...);
```
### UI обновления
- Кнопки завершения сессий теперь используют `terminateConnection()`
- Статус подключений отображается с `connectionId`
- Добавлены индикаторы состояния подключений
---
Для дополнительной информации см. исходный код:
- `/backend/src/managers/ConnectionManager.js`
- `/backend/src/server.js` (обработчики событий)
- `/desktop-operator/src/renderer/app.js` (клиентские методы)

View File

@@ -0,0 +1,72 @@
# Руководство по тестированию десктопного приложения GodEye
## Новые функции в десктопном приложении
### 1. Изменение кнопки подключения
- **До подключения**: кнопка показывает "Подключиться" (синий цвет)
- **После подключения**: кнопка показывает "Отключиться" (красный цвет)
- **Поведение**: кнопка переключается между подключением и отключением
### 2. Список активных сессий
- **Расположение**: правая панель, секция "Активные сессии"
- **Отображение**: показывает все сессии текущего оператора
- **Информация для каждой сессии**:
- ID устройства
- Тип камеры (back, front, wide, telephoto)
- Статус сессии (ожидание, активна, отклонена, завершена)
- Цветная индикация статуса
### 3. Управление множественными сессиями
- **Переключение между сессиями**: кнопка "Переключиться"
- **Переключение камеры**: кнопка "Фронтальная" для смены типа камеры
- **Завершение сессии**: кнопка "Завершить" для закрытия сессии
- **Активная сессия**: выделена зеленой рамкой
## Как протестировать
### Тестирование с веб-демо
1. Откройте веб-демо: http://localhost:3001
2. В разделе "Test Android Device" нажмите "Simulate Android Connection"
3. В десктопном приложении:
- Подключитесь к серверу (кнопка должна измениться на "Отключиться")
- В списке устройств должно появиться тестовое устройство
- Нажмите "Подключиться" к устройству
- В веб-демо примите запрос кнопкой "Accept Request"
- В десктопном приложении должна появиться активная сессия в списке сессий
### Тестирование множественных сессий
1. Создайте несколько сессий, повторяя процесс подключения
2. В списке сессий попробуйте:
- Переключиться между активными сессиями
- Переключить камеру в сессии
- Завершить одну из сессий
### Состояния сессий
- 🟠 **Ожидание** (pending): запрос отправлен, ожидается ответ от устройства
- 🟢 **Активна** (active): соединение установлено, видео передается
- 🔴 **Отклонена** (rejected): устройство отклонило запрос
-**Завершена** (ended): сессия была закрыта
## Улучшения пользовательского интерфейса
### Кнопки устройств
- Если с устройством есть активные сессии: показывает "Добавить сессию" (зеленая кнопка)
- Если нет активных сессий: показывает "Подключиться" (синяя кнопка)
### Индикаторы состояния
- Количество активных сессий отображается для каждого устройства
- Цветовая кодировка для быстрого визуального определения статуса
- Активная сессия выделена в списке
## События в журнале
Все действия фиксируются в журнале событий:
- Создание и принятие сессий
- Переключение между сессиями
- Переключение камер
- Завершение сессий
## Клавиатурные сокращения
- **Ctrl+S**: снимок экрана
- **Ctrl+F**: полноэкранный режим
- **Ctrl+R**: начать/остановить запись
- **ESC**: выйти из полноэкранного режима

View File

@@ -0,0 +1,112 @@
# Инструкция по тестированию исправленной системы сессий
## Проблемы, которые были исправлены:
### ✅ 1. Оператор не получал уведомления о новых запросах
**Исправление**: Добавлены правильные события `session:created`, `session:accepted`, `session:rejected`, `session:ended`
### ✅ 2. Сессии не отображались в приложении при подтверждении
**Исправление**: Обновлена обработка событий сессий в десктопном приложении
### ✅ 3. Правильная сущность сессий для переключения
**Исправление**: Полная реализация системы множественных сессий с переключением
## Последовательность тестирования:
### Шаг 1: Подключение к серверу
1. Запустите десктопное приложение
2. **НОВОЕ**: Панель подключения по умолчанию свернута с индикатором статуса 🔗 ● в заголовке
3. Кликните на заголовок "🔗 Подключение к серверу" чтобы развернуть панель
4. Нажмите "Подключиться" - кнопка должна изменить текст на "Отключиться"
5. **НОВОЕ**: Индикатор в заголовке изменится на зеленый ● для показа активного подключения
6. В журнале должно появиться сообщение о подключении
### Шаг 2: Симуляция Android устройства
1. В веб-демо (http://localhost:3001) найдите секцию "Test Android Device"
2. Нажмите "Simulate Android Connection"
3. В десктопном приложении должно появиться новое устройство в списке
### Шаг 3: Создание первой сессии
1. В десктопном приложении нажмите "Подключиться" к устройству
2. **НОВОЕ**: В списке сессий должна немедленно появиться сессия со статусом "Ожидание" (🟠)
3. В веб-демо появится запрос "Camera request from operator"
4. Нажмите "Accept Request" в веб-демо
5. **НОВОЕ**: Сессия в приложении должна изменить статус на "Активна" (🟢)
### Шаг 4: Создание множественных сессий
1. Повторите шаг 3 еще 2-3 раза для создания нескольких сессий
2. **НОВОЕ**: Кнопка устройства должна изменить текст на "Добавить сессию"
3. В списке сессий должны отображаться все активные сессии
### Шаг 5: Переключение между сессиями
1. Кликните "Переключиться" на любой неактивной сессии
2. **НОВОЕ**: Активная сессия должна выделиться зеленой рамкой
3. В верхней части должна отобразиться информация об активной сессии
### Шаг 6: Переключение камеры
1. В активной сессии нажмите "Фронтальная"
2. **НОВОЕ**: Тип камеры должен обновиться в информации о сессии
3. В журнале должно появиться сообщение о переключении
### Шаг 7: Завершение сессии
1. Нажмите "Завершить" на любой сессии
2. **НОВОЕ**: Сессия должна исчезнуть из списка
3. Если это была активная сессия, должна автоматически активироваться другая
## Новые события в журнале:
### События сессий:
- `Сессия создана: [sessionId]` - при запросе доступа к камере
- `Сессия принята: [sessionId]` - при подтверждении на устройстве
- `Сессия отклонена: [sessionId]` - при отказе на устройстве
- `Сессия завершена: [sessionId]` - при закрытии сессии
### События устройств:
- `Новое устройство подключено: [deviceId]` - при подключении Android
- `Устройство отключено: [deviceId]` - при отключении Android
### События управления:
- `Переключение на сессию: [sessionId]` - при смене активной сессии
- `Переключение на камеру: [type] в сессии [sessionId]` - при смене камеры
- `Завершение сессии: [sessionId]` - при инициировании закрытия
## Визуальные индикаторы:
### Статусы сессий:
- 🟠 **Ожидание** - запрос отправлен, ждем ответа от устройства
- 🟢 **Активна** - сессия установлена и работает
- 🔴 **Отклонена** - устройство отклонило запрос
-**Завершена** - сессия была закрыта
### Кнопки устройств:
- **"Подключиться"** (синяя) - нет активных сессий с устройством
- **"Добавить сессию"** (зеленая) - есть активные сессии, можно добавить еще
### Активная сессия:
- Выделена **зеленой рамкой** в списке
- Отображается в верхней части: `Активная сессия: [device] ([camera])`
- Кнопка показывает **"Активна"** вместо "Переключиться"
## Проверка исправлений:
### ✅ Проблема 1: Получение запросов
- Сессия должна появляться в списке **сразу** при нажатии "Подключиться"
- Статус должен меняться с "Ожидание" на "Активна" при подтверждении
### ✅ Проблема 2: Отображение сессий
- После "Accept Request" в веб-демо, сессия **немедленно** становится активной
- Все данные сессии корректно отображаются в списке
### ✅ Проблема 3: Переключение сессий
- Можно создать несколько активных сессий одновременно
- Переключение между сессиями работает мгновенно
- Управление камерами работает для каждой сессии независимо
## Ожидаемое поведение:
1. **Мгновенная отзывчивость** - все действия отражаются в UI немедленно
2. **Множественные сессии** - можно управлять несколькими устройствами одновременно
3. **Четкая индикация** - всегда ясно, какая сессия активна и каков ее статус
4. **Правильная синхронизация** - состояние между сервером и клиентом всегда синхронизировано
Теперь система сессий работает полноценно и позволяет эффективно управлять множественными подключениями!

View File

@@ -0,0 +1,116 @@
# Инструкция по тестированию улучшенного интерфейса
## Новые возможности интерфейса:
### ✅ 1. Компактное отображение устройств
- **Сокращенные ID**: длинные ID устройств обрезаются до 8 символов + "..."
- **Эмодзи-индикаторы**: 🟢 для онлайн, 🔴 для офлайн статуса
- **Компактные кнопки**: "🔗 Подключить" / " Добавить" вместо длинного текста
- **Иконки камер**: 📷 перед списком доступных камер
### ✅ 2. Сворачиваемый журнал событий
- **Кликабельный заголовок**: нажмите на "📋 Журнал событий" для сворачивания
- **Анимированная иконка**: ▲ (развернут) / ▼ (свернут)
- **Плавная анимация**: 0.3s transition для всех изменений
### ✅ 3. Адаптивные размеры панелей
- **При развернутых логах**: панель устройств/сессий ограничена высотой 180px
- **При свернутых логах**: панель увеличивается до 250px для лучшего обзора
- **Плавные переходы**: все изменения размеров анимированы
### ✅ 4. Улучшенное отображение сессий
- **Компактный формат**: "📱 device123... | 📷 back"
- **Эмодзи-статусы**: 🟠 Ожидание, 🟢 Активна, 🔴 Отклонена, ⚫ Завершена
- **Иконочные кнопки**: ✓ Активна, 🔄 Переключить, 📷 Камера, ❌ Завершить, 🗑️ Удалить
## Как протестировать:
### Тест 1: Сворачивание логов
1. Откройте десктопное приложение
2. Подключитесь к серверу (появятся сообщения в логах)
3. Кликните на заголовок "📋 Журнал событий"
4. **Ожидаемый результат**:
- Логи плавно скрываются
- Иконка меняется на ▼
- Панели устройств и сессий увеличиваются в высоту
### Тест 2: Компактные устройства
1. В веб-демо (http://localhost:3001) симулируйте Android устройство
2. В десктопном приложении должно появиться устройство с:
- Сокращенным ID (если длинный)
- Статусом 🟢 Онлайн
- Кнопкой "🔗 Подключить"
- Иконкой 📷 перед типами камер
### Тест 3: Множественные устройства
1. Симулируйте несколько устройств в веб-демо
2. **Проверьте**:
- Все устройства помещаются в компактную панель
- Прокрутка работает если устройств много
- Каждое устройство отображается корректно
### Тест 4: Компактные сессии
1. Создайте несколько сессий с разными устройствами
2. **Проверьте**:
- Сессии отображаются в формате "📱 device | 📷 camera"
- Статусы показываются эмодзи
- Кнопки имеют иконки вместо текста
- Активная сессия выделена зеленым
### Тест 5: Адаптивность интерфейса
1. Попробуйте сворачивать/разворачивать логи при разном количестве устройств/сессий
2. **Ожидаемый результат**:
- При свернутых логах больше места для списков
- Все анимации плавные
- Интерфейс остается читаемым
## Визуальные улучшения:
### Устройства:
```
📱 Устройства
🔄
┌─────────────────────────┐
│ ID: device12... │
│ Статус: 🟢 Онлайн │
│ Сессии: 2 │
│ 📷 back, front │
│ [ Добавить] │
└─────────────────────────┘
```
### Сессии:
```
🔗 Активные сессии
┌─────────────────────────┐
│ 📱 device123... | 📷 back │
│ 🟢 Активна │
│ [✓][📷][❌] │
└─────────────────────────┘
```
### Логи (сворачиваемые):
```
📋 Журнал событий ▲
┌─────────────────────────┐
│ [12:34:56] INFO: ... │
│ [12:34:57] SUCCESS: ... │
└─────────────────────────┘
[Очистить]
При клике на заголовок:
📋 Журнал событий ▼
[Очистить]
```
## Преимущества нового интерфейса:
1. **Больше информации** в том же пространстве
2. **Лучшая читаемость** благодаря иконкам
3. **Гибкость** - можно скрыть логи для больших списков
4. **Современный вид** с анимациями и эмодзи
5. **Быстрая навигация** по устройствам и сессиям
Теперь интерфейс готов для работы с большим количеством устройств и эффективного управления множественными сессиями! 🚀

107
docs/MOBILE_TEST_GUIDE.md Normal file
View File

@@ -0,0 +1,107 @@
# 📱 Руководство по тестированию мобильной версии GodEye
## 🚀 Запуск системы
1. **Сервер запущен** на: `http://localhost:3001`
2. **Мобильная версия**: `http://localhost:3001/mobile`
3. **Десктопная версия**: `http://localhost:3001/demo`
## 📱 Тестирование с телефона
### Вариант 1: Прямое подключение
1. Откройте браузер на телефоне
2. Перейдите по адресу: `http://[IP_СЕРВЕРА]:3001`
3. Система автоматически определит мобильное устройство и покажет мобильную версию
### Вариант 2: Принудительная мобильная версия
- Перейдите по адресу: `http://[IP_СЕРВЕРА]:3001/mobile`
## 🔧 Настройка доступа с телефона
### Узнать IP адрес сервера:
```bash
ip addr show | grep "inet.*192\|inet.*10\."
```
### Альтернативно (для локальной сети):
```bash
hostname -I
```
## 📋 Пошаговое тестирование
### 1. Подготовка
- ✅ Сервер запущен на порту 3001
- ✅ Мобильная версия создана
- ✅ Роуты настроены
### 2. Тестирование камеры на телефоне
1. Откройте `http://[IP]:3001` на телефоне
2. Разрешите доступ к камере
3. Проверьте переключение фронт/тыл камера
4. Проверьте отображение превью
### 3. Тестирование соединения с оператором
1. Откройте **Desktop Operator** (`/desktop-operator/`)
2. Подключитесь к серверу
3. Найдите мобильное устройство в списке
4. Попробуйте запросить доступ к камере
### 4. Тестирование WebRTC соединения
1. Оператор отправляет запрос
2. На телефоне появляется уведомление
3. Принимаете запрос на телефоне
4. Проверяете видеопоток в Desktop Operator
## 🌐 Доступные URL
- **Главная** (авто-определение): `http://[IP]:3001/`
- **Мобильная версия**: `http://[IP]:3001/mobile`
- **Десктоп демо**: `http://[IP]:3001/demo`
- **API статус**: `http://[IP]:3001/api/status`
## ✨ Возможности мобильной версии
### Интерфейс
- 📱 Адаптивный дизайн для телефонов
- 🎥 Превью камеры в реальном времени
- 🔄 Переключение фронт/тыл камера
- 📊 Статус соединения
- 🔔 Уведомления о запросах
### Функциональность
- 🔌 WebSocket соединение с сервером
- 📹 WebRTC для передачи видео
- 🎯 Обработка запросов от операторов
- ✅ Принятие/отклонение запросов
- 🔄 Поддержка нескольких сессий
## 🐛 Отладка
### Проверка соединения
1. Откройте консоль разработчика в браузере
2. Проверьте WebSocket соединение
3. Проверьте ошибки WebRTC
### Логи сервера
```bash
tail -f /home/data/god_eye/backend/god-eye.log
```
### Проверка устройств
```bash
curl http://localhost:3001/api/status
```
## 📝 Примечания
- **HTTPS**: Для работы камеры в production нужен HTTPS
- **Локальная сеть**: В локальной сети работает через HTTP
- **Порты**: Убедитесь, что порт 3001 открыт в файрволе
- **Браузер**: Рекомендуется Chrome/Safari для лучшей поддержки WebRTC
## 🚨 Известные ограничения
1. **HTTP vs HTTPS**: Камера работает только на localhost через HTTP или везде через HTTPS
2. **Файрвол**: Нужно открыть порт 3001 для внешних подключений
3. **WebRTC**: Некоторые сетевые настройки могут блокировать P2P соединения

View File

@@ -0,0 +1,288 @@
# Руководство: Подключение Desktop Operator к Android телефону
## 🔗 Как работает подключение оператора к телефону
### Архитектура подключения
```
Desktop Operator ←→ WebSocket ←→ Сервер ←→ WebSocket ←→ Android телефон
ConnectionManager
(управляет соединениями)
WebRTC P2P соединение
(прямая передача видео)
```
### 1. 📱 Регистрация Android устройства
**Android телефон подключается к серверу:**
```kotlin
// Android код
val socket = IO.socket("ws://192.168.1.100:3001")
socket.emit("register:android", JSONObject().apply {
put("deviceId", "android_unique_id")
put("deviceInfo", JSONObject().apply {
put("manufacturer", "Samsung")
put("model", "Galaxy S21")
put("androidVersion", "11")
put("availableCameras", JSONArray(listOf("back", "front", "ultra_wide")))
put("appVersion", "1.0.0")
})
})
```
**Сервер подтверждает регистрацию:**
```javascript
// Лог сервера
{"level":"info","message":"Android client registered: android_unique_id","timestamp":"..."}
```
### 2. 💻 Подключение Desktop Operator
**Operator приложение подключается:**
```javascript
// Desktop Operator код
const socket = io('ws://192.168.1.100:3001');
socket.emit('register:operator', {
operatorId: 'operator-uuid',
operatorInfo: {
name: 'Иван Петров',
organization: 'Служба безопасности',
permissions: ['view_cameras', 'request_camera', 'initiate_connection']
}
});
```
### 3. 🔍 Проверка доступных устройств
**REST API запрос списка устройств:**
```bash
curl -H "x-operator-id: operator-uuid" \
http://localhost:3001/api/operators/devices
```
**Ответ сервера:**
```json
{
"success": true,
"devices": [
{
"deviceId": "android_unique_id",
"model": "Galaxy S21",
"manufacturer": "Samsung",
"isConnected": true,
"availableCameras": ["back", "front", "ultra_wide"],
"canAcceptNewSession": true,
"activeSessions": 0
}
],
"total": 1
}
```
### 4. 📞 Инициация подключения к телефону
**Оператор запрашивает доступ к камере:**
```bash
curl -X POST http://localhost:3001/api/operators/connections/request \
-H "Content-Type: application/json" \
-H "x-operator-id: operator-uuid" \
-d '{
"deviceId": "android_unique_id",
"cameraType": "back"
}'
```
**Сервер создает подключение:**
```json
{
"success": true,
"connectionId": "conn-uuid",
"sessionId": "session-uuid",
"message": "Connection request initiated"
}
```
### 5. 📲 Android получает запрос подтверждения
**WebSocket событие на Android:**
```javascript
// Сервер → Android
socket.emit('connection:request', {
connectionId: 'conn-uuid',
sessionId: 'session-uuid',
operatorId: 'operator-uuid',
operatorInfo: {
name: 'Иван Петров',
organization: 'Служба безопасности',
reason: 'Проверка безопасности'
},
cameraType: 'back',
timestamp: '2025-10-04T12:00:00.000Z',
expiresAt: '2025-10-04T12:05:00.000Z' // 5 минут на ответ
});
```
**Android показывает диалог пользователю:**
```
┌─────────────────────────────────┐
│ Запрос доступа к камере │
├─────────────────────────────────┤
│ Оператор: Иван Петров │
│ Организация: Служба безопасности│
│ Причина: Проверка безопасности │
│ Камера: Задняя │
│ │
│ Разрешить доступ к камере? │
│ │
│ [Разрешить] [Отклонить] │
└─────────────────────────────────┘
```
### 6. ✅ Пользователь принимает/отклоняет запрос
**Если пользователь принимает:**
```kotlin
// Android → Сервер
socket.emit("connection:accept", JSONObject().apply {
put("connectionId", connectionId)
put("sessionId", sessionId)
put("cameraType", "back")
put("webrtcInfo", JSONObject().apply {
put("supported", true)
put("codecs", JSONArray(listOf("H264", "VP8")))
put("resolutions", JSONArray(listOf("720p", "1080p")))
})
})
```
**Сервер уведомляет оператора:**
```javascript
// Сервер → Desktop Operator
socket.emit('connection:accepted', {
connectionId: 'conn-uuid',
sessionId: 'session-uuid',
webrtcInfo: {
supported: true,
codecs: ['H264', 'VP8'],
resolutions: ['720p', '1080p']
}
});
```
### 7. 🎥 Установка WebRTC соединения
**Обмен WebRTC сигналами:**
```
Desktop Operator → Сервер → Android: webrtc:offer
Android → Сервер → Desktop Operator: webrtc:answer
Desktop Operator ↔ Сервер ↔ Android: webrtc:ice-candidate
```
**После успешного установления WebRTC:**
```
Desktop Operator ←-----WebRTC P2P-----→ Android
(прямое видео)
```
## 🧪 Тестирование подключения
### Проверка 1: Статус системы
```bash
curl http://localhost:3001/api/status | jq
```
### Проверка 2: Список устройств
```bash
curl -H "x-operator-id: YOUR_OPERATOR_ID" \
http://localhost:3001/api/operators/devices | jq
```
### Проверка 3: Инициация подключения
```bash
curl -X POST http://localhost:3001/api/operators/connections/request \
-H "Content-Type: application/json" \
-H "x-operator-id: YOUR_OPERATOR_ID" \
-d '{
"deviceId": "android_device_id",
"cameraType": "back"
}' | jq
```
### Проверка 4: Статус подключений
```bash
curl -H "x-operator-id: YOUR_OPERATOR_ID" \
http://localhost:3001/api/operators/connections | jq
```
## 🐞 Диагностика проблем
### Проблема: "Invalid or disconnected operator"
**Причина:** Оператор не подключен к WebSocket
**Решение:**
1. Откройте веб-демо: http://localhost:3001
2. Проверьте WebSocket подключение в консоли браузера
3. Используйте Operator ID из консоли
### Проблема: "Device not found"
**Причина:** Android устройство не подключено
**Решение:**
1. Перезапустите Android приложение
2. Проверьте сетевое соединение
3. Убедитесь что устройство регистрируется
### Проблема: "Device busy or unavailable"
**Причина:** Устройство занято другой сессией
**Решение:**
1. Завершите активные сессии
2. Подождите таймаут (30 секунд)
3. Попробуйте снова
## 📊 Мониторинг соединений
### Логи сервера
```bash
tail -f /home/data/god_eye/backend/god-eye.log
```
### Административная статистика
```bash
curl http://localhost:3001/api/admin/stats | jq
```
### Real-time статус через WebSocket
```javascript
socket.on('device:connected', (data) => {
console.log('Устройство подключено:', data.deviceId);
});
socket.on('connection:accepted', (data) => {
console.log('Подключение принято:', data.connectionId);
});
```
## 🔒 Безопасность
1. **Разрешения пользователя:** Android требует подтверждения каждого запроса
2. **Таймауты:** Запросы автоматически истекают через 5 минут
3. **Аутентификация:** Проверка Operator ID для всех API запросов
4. **Логирование:** Все операции записываются в логи
## 🚀 Быстрый старт
1. **Запустите сервер:**
```bash
cd /home/data/god_eye/backend && npm start
```
2. **Откройте веб-демо:**
http://localhost:3001
3. **Запустите Android приложение** и подключите к серверу
4. **Инициируйте подключение** через веб-интерфейс или API
5. **Примите запрос** на Android устройстве
6. **Наслаждайтесь** прямым WebRTC видеопотоком!

View File

@@ -0,0 +1,484 @@
# Протокол запросов от сервера к Android устройству
## Обзор
Данный документ описывает протокол WebSocket событий, которые сервер отправляет на Android устройство для запроса подтверждения подключения оператора и открытия сеанса камеры.
## Схема работы
1. **Оператор инициирует подключение** через REST API или WebSocket
2. **Сервер создает соединение** в ConnectionManager
3. **Сервер отправляет запрос на Android** через WebSocket
4. **Android отображает диалог** пользователю
5. **Пользователь принимает/отклоняет** запрос
6. **Android отправляет ответ** серверу
7. **Сервер уведомляет оператора** о результате
## События от сервера к Android
### 1. `connection:request` - Запрос на подключение
Отправляется Android устройству когда оператор запрашивает доступ к камере.
```javascript
// Сервер → Android
socket.emit('connection:request', {
connectionId: 'uuid-connection-id',
sessionId: 'uuid-session-id',
operatorId: 'uuid-operator-id',
operatorInfo: {
name: 'Имя оператора',
organization: 'Организация',
reason: 'Причина запроса доступа'
},
cameraType: 'back', // 'back', 'front', 'wide', 'telephoto'
timestamp: '2025-10-04T12:00:00.000Z',
expiresAt: '2025-10-04T12:05:00.000Z' // Время истечения запроса (5 минут)
});
```
**Ожидаемый ответ от Android:**
- `connection:accept` - пользователь принял запрос
- `connection:reject` - пользователь отклонил запрос
- Timeout через 5 минут если нет ответа
### 2. `camera:request` - Запрос доступа к камере (Legacy)
Для совместимости со старой системой. Используется при прямом запросе камеры.
```javascript
// Сервер → Android
socket.emit('camera:request', {
sessionId: 'uuid-session-id',
operatorId: 'uuid-operator-id',
cameraType: 'back',
timestamp: '2025-10-04T12:00:00.000Z'
});
```
### 3. `camera:switch` - Переключение камеры
Запрос на переключение камеры во время активного сеанса.
```javascript
// Сервер → Android
socket.emit('camera:switch', {
sessionId: 'uuid-session-id',
cameraType: 'front', // Новый тип камеры
timestamp: '2025-10-04T12:00:00.000Z'
});
```
### 4. `camera:disconnect` - Завершение сеанса
Уведомление о завершении сеанса камеры.
```javascript
// Сервер → Android
socket.emit('camera:disconnect', {
sessionId: 'uuid-session-id',
reason: 'operator_disconnect', // 'operator_disconnect', 'timeout', 'error'
timestamp: '2025-10-04T12:00:00.000Z'
});
```
## События от Android к серверу
### 1. `connection:accept` - Принятие подключения
```javascript
// Android → Сервер
socket.emit('connection:accept', {
connectionId: 'uuid-connection-id',
sessionId: 'uuid-session-id',
cameraType: 'back',
webrtcInfo: {
supported: true,
codecs: ['H264', 'VP8'],
resolutions: ['720p', '1080p']
},
timestamp: '2025-10-04T12:00:00.000Z'
});
```
### 2. `connection:reject` - Отклонение подключения
```javascript
// Android → Сервер
socket.emit('connection:reject', {
connectionId: 'uuid-connection-id',
reason: 'user_denied', // 'user_denied', 'camera_busy', 'permission_denied'
timestamp: '2025-10-04T12:00:00.000Z'
});
```
### 3. `camera:response` - Ответ на запрос камеры (Legacy)
```javascript
// Android → Сервер
socket.emit('camera:response', {
sessionId: 'uuid-session-id',
accepted: true, // true/false
reason: 'camera_granted', // или причина отказа
cameraType: 'back',
timestamp: '2025-10-04T12:00:00.000Z'
});
```
## Жизненный цикл подключения
### Успешное подключение
```mermaid
sequenceDiagram
participant O as Оператор
participant S as Сервер
participant A as Android
O->>S: POST /api/operators/connections/request
S->>S: Создание connection в ConnectionManager
S->>A: connection:request
A->>A: Показ диалога пользователю
A->>S: connection:accept
S->>S: Обновление connection (status: accepted)
S->>O: connection:accepted (WebSocket)
Note over O,A: Начало WebRTC сеанса
```
### Отклонение подключения
```mermaid
sequenceDiagram
participant O as Оператор
participant S as Сервер
participant A as Android
O->>S: POST /api/operators/connections/request
S->>S: Создание connection в ConnectionManager
S->>A: connection:request
A->>A: Показ диалога пользователю
A->>S: connection:reject
S->>S: Обновление connection (status: rejected)
S->>O: connection:rejected (WebSocket)
```
### Таймаут подключения
```mermaid
sequenceDiagram
participant O as Оператор
participant S as Сервер
participant A as Android
O->>S: POST /api/operators/connections/request
S->>S: Создание connection в ConnectionManager
S->>A: connection:request
Note over A: Пользователь не отвечает
S->>S: Timeout через 5 минут
S->>S: Обновление connection (status: timeout)
S->>O: connection:timeout (WebSocket)
S->>A: connection:timeout (уведомление)
```
## Обработка в ConnectionManager
### Инициация подключения
```javascript
// В файле /backend/src/managers/ConnectionManager.js
async initiateConnection(operatorId, deviceId, cameraType = 'back') {
// 1. Создание connection объекта
const connection = {
connectionId: uuidv4(),
sessionId: uuidv4(),
operatorId,
deviceId,
cameraType,
status: 'pending',
createdAt: new Date().toISOString(),
expiresAt: new Date(Date.now() + 5 * 60 * 1000).toISOString()
};
// 2. Сохранение в память
this.connections.set(connection.connectionId, connection);
// 3. Отправка запроса на Android
const device = this.deviceManager.getDevice(deviceId);
device.socket.emit('connection:request', {
connectionId: connection.connectionId,
sessionId: connection.sessionId,
operatorId,
operatorInfo: operator.operatorInfo,
cameraType,
timestamp: connection.createdAt,
expiresAt: connection.expiresAt
});
// 4. Установка таймаута
setTimeout(() => {
if (connection.status === 'pending') {
this.handleConnectionTimeout(connection.connectionId);
}
}, 5 * 60 * 1000);
return connection;
}
```
### Принятие подключения
```javascript
async acceptConnection(connectionId, responseData) {
const connection = this.connections.get(connectionId);
// Обновление статуса
connection.status = 'accepted';
connection.acceptedAt = new Date().toISOString();
connection.webrtcInfo = responseData.webrtcInfo;
// Создание сессии
const session = this.sessionManager.createSession(
connection.deviceId,
connection.operatorId,
connection.cameraType
);
// Уведомление оператора
const operator = this.deviceManager.getOperator(connection.operatorId);
operator.socket.emit('connection:accepted', {
connectionId,
sessionId: session.sessionId,
webrtcInfo: connection.webrtcInfo
});
return connection;
}
```
## Обработка в Android приложении
### Регистрация обработчиков событий
```kotlin
// В Android клиенте
socket.on("connection:request") { args ->
val data = args[0] as JSONObject
val connectionId = data.getString("connectionId")
val operatorInfo = data.getJSONObject("operatorInfo")
val cameraType = data.getString("cameraType")
// Показ диалога пользователю
showConnectionRequestDialog(connectionId, operatorInfo, cameraType)
}
socket.on("camera:request") { args ->
val data = args[0] as JSONObject
val sessionId = data.getString("sessionId")
val operatorId = data.getString("operatorId")
val cameraType = data.getString("cameraType")
// Legacy обработка для совместимости
handleCameraRequest(sessionId, operatorId, cameraType)
}
```
### Ответ на запрос подключения
```kotlin
private fun acceptConnection(connectionId: String, cameraType: String) {
val response = JSONObject().apply {
put("connectionId", connectionId)
put("sessionId", sessionId)
put("cameraType", cameraType)
put("webrtcInfo", JSONObject().apply {
put("supported", true)
put("codecs", JSONArray(listOf("H264", "VP8")))
put("resolutions", JSONArray(listOf("720p", "1080p")))
})
put("timestamp", Instant.now().toString())
}
socket.emit("connection:accept", response)
// Начало подготовки камеры
startCameraPreview(cameraType)
}
private fun rejectConnection(connectionId: String, reason: String) {
val response = JSONObject().apply {
put("connectionId", connectionId)
put("reason", reason)
put("timestamp", Instant.now().toString())
}
socket.emit("connection:reject", response)
}
```
## UI диалог на Android
### Пример диалога подтверждения
```kotlin
private fun showConnectionRequestDialog(
connectionId: String,
operatorInfo: JSONObject,
cameraType: String
) {
val dialog = AlertDialog.Builder(this)
.setTitle("Запрос доступа к камере")
.setMessage("""
Оператор: ${operatorInfo.getString("name")}
Организация: ${operatorInfo.getString("organization")}
Причина: ${operatorInfo.getString("reason")}
Камера: ${getCameraDisplayName(cameraType)}
Разрешить доступ к камере?
""".trimIndent())
.setPositiveButton("Разрешить") { _, _ ->
acceptConnection(connectionId, cameraType)
}
.setNegativeButton("Отклонить") { _, _ ->
rejectConnection(connectionId, "user_denied")
}
.setCancelable(false)
.create()
dialog.show()
// Автоматическое закрытие через 5 минут
Handler(Looper.getMainLooper()).postDelayed({
if (dialog.isShowing) {
dialog.dismiss()
rejectConnection(connectionId, "timeout")
}
}, 5 * 60 * 1000)
}
```
## Безопасность и валидация
### Проверки на сервере
1. **Валидация оператора**: проверка разрешений и статуса подключения
2. **Валидация устройства**: проверка доступности и возможности принять сессию
3. **Лимиты времени**: автоматическое завершение запросов через 5 минут
4. **Лимиты сессий**: проверка максимального количества активных сессий
### Проверки на Android
1. **Валидация connectionId**: проверка существования активного запроса
2. **Проверка разрешений**: доступ к камере и микрофону
3. **Проверка состояния**: доступность камеры для использования
4. **Защита от спама**: лимит на количество запросов в минуту
## Логирование и мониторинг
### События для логирования
```javascript
// Сервер
logger.info('Connection request initiated', {
connectionId,
operatorId,
deviceId,
cameraType
});
logger.info('Connection accepted by device', {
connectionId,
sessionId,
responseTime: Date.now() - connection.createdAt
});
logger.warn('Connection rejected by device', {
connectionId,
reason,
operatorId,
deviceId
});
logger.error('Connection timeout', {
connectionId,
operatorId,
deviceId,
duration: 5 * 60 * 1000
});
```
### Метрики для мониторинга
- Время ответа Android устройств на запросы
- Процент принятых/отклоненных подключений
- Количество таймаутов
- Средняя продолжительность сессий
- Ошибки WebRTC соединений
## Совместимость
Система поддерживает как новый протокол подключений (`connection:*` события), так и старый протокол (`camera:*` события) для обратной совместимости.
### Миграция со старого протокола
1. **Этап 1**: Добавление поддержки новых событий в Android
2. **Этап 2**: Постепенный переход операторов на новый API
3. **Этап 3**: Удаление старых обработчиков после полной миграции
## Примеры использования
### Тестирование через WebSocket
```javascript
// Подключение к серверу
const socket = io('ws://localhost:3001');
// Симуляция Android устройства
socket.emit('register:android', {
deviceId: 'test-device-001',
deviceInfo: {
manufacturer: 'Samsung',
model: 'Galaxy S21',
availableCameras: ['back', 'front'],
androidVersion: '11'
}
});
// Обработка запросов
socket.on('connection:request', (data) => {
console.log('Получен запрос подключения:', data);
// Автоматическое принятие для тестирования
setTimeout(() => {
socket.emit('connection:accept', {
connectionId: data.connectionId,
sessionId: data.sessionId,
cameraType: data.cameraType,
webrtcInfo: {
supported: true,
codecs: ['H264'],
resolutions: ['1080p']
}
});
}, 2000);
});
```
### Тестирование через REST API
```bash
# Инициация подключения
curl -X POST http://localhost:3001/api/operators/connections/request \
-H "Content-Type: application/json" \
-H "x-operator-id: operator-uuid" \
-d '{
"deviceId": "test-device-001",
"cameraType": "back"
}'
# Проверка статуса подключений
curl -H "x-operator-id: operator-uuid" \
http://localhost:3001/api/operators/connections
```
Этот протокол обеспечивает надежную и безопасную систему запросов доступа к камере Android устройств с полным контролем пользователя над разрешениями.

View File

@@ -0,0 +1,479 @@
# Техническое задание и промпт для создания Android приложения GodEye Signal Center
## 🎯 ТЕХНИЧЕСКОЕ ЗАДАНИЕ
### Назначение системы
Создать Android приложение для системы удаленного доступа к камерам смартфона "GodEye Signal Center". Приложение должно предоставлять операторам доступ к камерам устройства через WebRTC с возможностью переключения между различными типами камер.
### Архитектура системы
```
[Android App] ←→ [WebSocket/Socket.IO] ←→ [Backend Server] ←→ [Desktop Operator]
←→ [WebRTC P2P Connection] ←→ [Desktop Operator]
```
### Основные компоненты
1. **MainActivity** - главный экран с управлением подключением
2. **SocketService** - сервис для WebSocket соединения с backend
3. **CameraManager** - управление камерами устройства (Camera2 API)
4. **WebRTCManager** - обработка WebRTC соединений для видеопотока
5. **PermissionManager** - управление разрешениями приложения
6. **SessionManager** - управление активными сессиями с операторами
### Функциональные требования
- ✅ Подключение к backend серверу по WebSocket (Socket.IO)
- ✅ Регистрация устройства с передачей характеристик
- ✅ Получение и обработка запросов доступа к камере от операторов
- ✅ Диалоги согласия пользователя на доступ к камере
- ✅ WebRTC соединение для передачи видеопотока
- ✅ Переключение между камерами: основная, фронтальная, широкоугольная, телеобъектив
- ✅ Отображение активных сессий и их управление
- ✅ Автоматическое переподключение при обрыве соединения
- ✅ Работа в фоне (Foreground Service)
- ✅ Уведомления о статусе подключения
### Технические требования
- **Платформа**: Android API 24+ (Android 7.0+)
- **Язык**: Kotlin
- **Архитектура**: MVVM с LiveData
- **Сеть**: Socket.IO для сигнализации, WebRTC для медиа
- **Камера**: Camera2 API для работы с камерами
- **UI**: Material Design 3
- **Разрешения**: CAMERA, RECORD_AUDIO, INTERNET, FOREGROUND_SERVICE
## 📋 ПОЛНЫЙ ПРОМПТ ДЛЯ COPILOT
---
**Создай полное Android приложение на Kotlin для системы GodEye Signal Center со следующими требованиями:**
### 🚀 ОСНОВНАЯ ЗАДАЧА
Разработать Android приложение, которое подключается к backend серверу, получает запросы от операторов на доступ к камере устройства и предоставляет видеопоток через WebRTC с возможностью переключения между камерами.
### 📱 СТРУКТУРА ПРОЕКТА
```
app/
├── src/main/
│ ├── java/com/godeye/android/
│ │ ├── MainActivity.kt
│ │ ├── services/
│ │ │ ├── SocketService.kt
│ │ │ └── CameraService.kt
│ │ ├── managers/
│ │ │ ├── CameraManager.kt
│ │ │ ├── WebRTCManager.kt
│ │ │ ├── SessionManager.kt
│ │ │ └── PermissionManager.kt
│ │ ├── models/
│ │ │ ├── DeviceInfo.kt
│ │ │ ├── CameraSession.kt
│ │ │ └── SocketEvents.kt
│ │ ├── ui/
│ │ │ ├── dialogs/
│ │ │ │ └── CameraRequestDialog.kt
│ │ │ └── adapters/
│ │ │ └── SessionAdapter.kt
│ │ └── utils/
│ │ ├── Constants.kt
│ │ └── Extensions.kt
│ ├── res/
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ ├── dialog_camera_request.xml
│ │ │ └── item_session.xml
│ │ ├── values/
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ └── themes.xml
│ │ └── drawable/
│ └── AndroidManifest.xml
└── build.gradle (app)
```
### 🔧 BACKEND API ENDPOINTS
#### WebSocket События (Socket.IO на порту 3001):
```kotlin
// Регистрация устройства
socket.emit("register:android", JsonObject().apply {
addProperty("deviceId", deviceId)
add("deviceInfo", JsonObject().apply {
addProperty("model", Build.MODEL)
addProperty("androidVersion", Build.VERSION.RELEASE)
addProperty("appVersion", "1.0.0")
add("availableCameras", JsonArray().apply {
add("back"); add("front"); add("wide"); add("telephoto")
})
})
})
// Обработка входящих событий
socket.on("register:success") { /* успешная регистрация */ }
socket.on("camera:request") { /* запрос доступа к камере */ }
socket.on("camera:disconnect") { /* завершение сессии */ }
socket.on("camera:switch") { /* переключение камеры */ }
socket.on("webrtc:offer") { /* WebRTC offer от оператора */ }
socket.on("webrtc:ice-candidate") { /* ICE candidates */ }
// Отправка ответов
socket.emit("camera:response", JsonObject().apply {
addProperty("sessionId", sessionId)
addProperty("accepted", true)
})
socket.emit("webrtc:answer", JsonObject().apply {
addProperty("sessionId", sessionId)
add("answer", /* RTCSessionDescription */)
})
```
### 🎨 UI/UX ТРЕБОВАНИЯ
#### MainActivity.xml:
```xml
<!-- Основной экран с полями: -->
- TextView: Device ID (автогенерируемый)
- EditText: Server URL (по умолчанию: http://192.168.1.100:3001)
- TextView: Connection Status
- Button: Connect/Disconnect
- RecyclerView: Active Sessions
- ProgressBar: Connection progress
- FloatingActionButton: Settings
```
#### CameraRequestDialog.xml:
```xml
<!-- Диалог запроса доступа: -->
- ImageView: Operator avatar/icon
- TextView: "Оператор {operatorId} запрашивает доступ к камере {cameraType}"
- TextView: Session ID
- Button: "Разрешить" / "Отклонить"
- CheckBox: "Запомнить для этого оператора"
```
### 💾 DATA MODELS
```kotlin
data class DeviceInfo(
val model: String,
val androidVersion: String,
val appVersion: String,
val availableCameras: List<String>
)
data class CameraSession(
val sessionId: String,
val operatorId: String,
val cameraType: String,
val startTime: Long,
var isActive: Boolean = true,
var webRTCConnected: Boolean = false
)
sealed class SocketEvent {
data class RegisterAndroid(val deviceId: String, val deviceInfo: DeviceInfo) : SocketEvent()
data class CameraRequest(val sessionId: String, val operatorId: String, val cameraType: String) : SocketEvent()
data class CameraResponse(val sessionId: String, val accepted: Boolean) : SocketEvent()
data class WebRTCOffer(val sessionId: String, val offer: String) : SocketEvent()
data class WebRTCAnswer(val sessionId: String, val answer: String) : SocketEvent()
}
```
### 🔐 РАЗРЕШЕНИЯ (AndroidManifest.xml):
```xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature android:name="android.hardware.microphone" android:required="true" />
```
### 📚 DEPENDENCIES (build.gradle):
```gradle
dependencies {
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.7.0'
implementation 'androidx.activity:activity-ktx:1.8.2'
// Socket.IO для WebSocket соединения
implementation 'io.socket:socket.io-client:2.1.0'
// WebRTC для видеопотока
implementation 'org.webrtc:google-webrtc:1.0.32006'
// Camera2 API
implementation 'androidx.camera:camera-core:1.3.1'
implementation 'androidx.camera:camera-camera2:1.3.1'
implementation 'androidx.camera:camera-lifecycle:1.3.1'
implementation 'androidx.camera:camera-view:1.3.1'
// JSON парсинг
implementation 'com.google.code.gson:gson:2.10.1'
// Корутины
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
// RecyclerView
implementation 'androidx.recyclerview:recyclerview:1.3.2'
// Work Manager для фоновых задач
implementation 'androidx.work:work-runtime-ktx:2.9.0'
}
```
### 🏗️ ОСНОВНЫЕ КЛАССЫ
#### 1. MainActivity.kt - Основная логика:
```kotlin
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var socketService: SocketService
private lateinit var sessionAdapter: SessionAdapter
private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
// Инициализация UI
// Проверка разрешений
// Настройка RecyclerView для сессий
// Подключение к SocketService
}
private fun connectToServer() {
val serverUrl = binding.etServerUrl.text.toString()
socketService.connect(serverUrl)
}
private fun showCameraRequestDialog(request: CameraRequest) {
CameraRequestDialog.newInstance(request).show(supportFragmentManager, "camera_request")
}
}
```
#### 2. SocketService.kt - WebSocket соединение:
```kotlin
class SocketService : Service() {
private lateinit var socket: Socket
private val binder = LocalBinder()
fun connect(serverUrl: String) {
socket = IO.socket(serverUrl)
socket.connect()
registerDevice()
setupEventListeners()
}
private fun registerDevice() {
val deviceInfo = DeviceInfo(
model = Build.MODEL,
androidVersion = Build.VERSION.RELEASE,
appVersion = BuildConfig.VERSION_NAME,
availableCameras = CameraManager.getAvailableCameraTypes()
)
socket.emit("register:android", gson.toJson(RegisterAndroid(deviceId, deviceInfo)))
}
private fun setupEventListeners() {
socket.on("camera:request") { args ->
val request = gson.fromJson(args[0].toString(), CameraRequest::class.java)
// Отправить broadcast для показа диалога
}
}
}
```
#### 3. CameraManager.kt - Управление камерами:
```kotlin
class CameraManager(private val context: Context) {
private val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as android.hardware.camera2.CameraManager
private var currentCameraId: String? = null
private var captureSession: CameraCaptureSession? = null
fun getAvailableCameraTypes(): List<String> {
val cameras = mutableListOf<String>()
try {
for (cameraId in cameraManager.cameraIdList) {
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
val facing = characteristics.get(CameraCharacteristics.LENS_FACING)
when (facing) {
CameraCharacteristics.LENS_FACING_BACK -> cameras.add("back")
CameraCharacteristics.LENS_FACING_FRONT -> cameras.add("front")
}
// Проверка на wide и telephoto
}
} catch (e: Exception) {
Log.e("CameraManager", "Error getting cameras", e)
}
return cameras
}
@SuppressLint("MissingPermission")
fun startCamera(cameraType: String, surface: Surface) {
val cameraId = getCameraIdForType(cameraType) ?: return
cameraManager.openCamera(cameraId, cameraStateCallback, null)
}
}
```
#### 4. WebRTCManager.kt - WebRTC соединения:
```kotlin
class WebRTCManager(private val context: Context) {
private lateinit var peerConnectionFactory: PeerConnectionFactory
private var peerConnection: RTCPeerConnection? = null
private var localVideoSource: VideoSource? = null
fun initialize() {
val initializationOptions = PeerConnectionFactory.InitializationOptions.builder(context)
.setEnableInternalTracer(false)
.createInitializationOptions()
PeerConnectionFactory.initialize(initializationOptions)
val options = PeerConnectionFactory.Options()
peerConnectionFactory = PeerConnectionFactory.builder()
.setOptions(options)
.createPeerConnectionFactory()
}
fun createOffer(sessionId: String) {
val mediaConstraints = MediaConstraints()
peerConnection?.createOffer(object : SdpObserver {
override fun onCreateSuccess(sessionDescription: SessionDescription) {
// Отправить offer через WebSocket
}
}, mediaConstraints)
}
fun handleAnswer(sessionId: String, answer: SessionDescription) {
peerConnection?.setRemoteDescription(SimpleSdpObserver(), answer)
}
}
```
### 🔄 WORKFLOW ПРИЛОЖЕНИЯ
1. **Запуск приложения:**
- Проверка разрешений (CAMERA, AUDIO, INTERNET)
- Генерация уникального Device ID
- Определение доступных камер устройства
- Инициализация WebRTC
2. **Подключение к серверу:**
- Подключение Socket.IO к указанному URL
- Регистрация устройства с отправкой характеристик
- Ожидание подтверждения регистрации
- Запуск Foreground Service для фоновой работы
3. **Обработка запроса камеры:**
- Получение события "camera:request" от сервера
- Показ диалога пользователю с информацией об операторе
- При согласии - отправка "camera:response" с accepted: true
- Инициализация WebRTC соединения
4. **WebRTC соединение:**
- Создание RTCPeerConnection
- Настройка локального видеопотока с указанной камеры
- Обмен offer/answer/ice-candidates с оператором
- Начало передачи видеопотока
5. **Управление сессией:**
- Отображение активных сессий в RecyclerView
- Обработка команд переключения камеры
- Завершение сессии по команде оператора или пользователя
- Очистка ресурсов WebRTC
### ⚙️ НАСТРОЙКИ И КОНФИГУРАЦИЯ
#### SharedPreferences ключи:
```kotlin
object PreferenceKeys {
const val SERVER_URL = "server_url"
const val DEVICE_ID = "device_id"
const val AUTO_ACCEPT_REQUESTS = "auto_accept_requests"
const val CAMERA_QUALITY = "camera_quality"
const val NOTIFICATION_ENABLED = "notification_enabled"
}
```
#### Настройки WebRTC:
```kotlin
val iceServers = listOf(
RTCIceServer.builder("stun:stun.l.google.com:19302").build(),
RTCIceServer.builder("stun:stun1.l.google.com:19302").build()
)
val rtcConfig = RTCConfiguration(iceServers).apply {
tcpCandidatePolicy = RTCConfiguration.TcpCandidatePolicy.DISABLED
bundlePolicy = RTCConfiguration.BundlePolicy.MAXBUNDLE
rtcpMuxPolicy = RTCConfiguration.RtcpMuxPolicy.REQUIRE
}
```
### 🐛 ОБРАБОТКА ОШИБОК
```kotlin
sealed class AppError {
object NetworkError : AppError()
object CameraPermissionDenied : AppError()
object CameraNotAvailable : AppError()
object WebRTCConnectionFailed : AppError()
data class SocketError(val message: String) : AppError()
data class UnknownError(val throwable: Throwable) : AppError()
}
class ErrorHandler {
fun handleError(error: AppError, context: Context) {
when (error) {
is AppError.NetworkError -> showNetworkError(context)
is AppError.CameraPermissionDenied -> showPermissionDialog(context)
is AppError.WebRTCConnectionFailed -> restartWebRTC()
// и т.д.
}
}
}
```
### 📋 ТЕСТИРОВАНИЕ
1. **Запуск backend сервера**: `cd backend && node src/server.js`
2. **Веб-демо оператора**: `http://localhost:3001`
3. **Подключение Android**: указать URL `http://192.168.1.100:3001`
4. **Тестовые сценарии**:
- Регистрация устройства
- Запрос доступа к камере от веб-демо
- Переключение между камерами
- Разрыв и восстановление соединения
### 🚀 ФИНАЛЬНЫЕ ТРЕБОВАНИЯ
**Создай полный рабочий проект со всеми перечисленными файлами, который:**
- ✅ Компилируется без ошибок на API 24+
- ✅ Корректно подключается к backend серверу
- ✅ Отображает запросы операторов в диалогах
- ✅ Передает видеопоток через WebRTC
- ✅ Поддерживает переключение камер
- ✅ Работает в фоне с уведомлениями
- ✅ Имеет современный Material Design UI
- ✅ Обрабатывает все ошибки и исключения
**Backend server URL для тестирования**: `http://localhost:3001` или `http://192.168.1.100:3001`
---
### 📝 ДОПОЛНИТЕЛЬНЫЕ ПРИМЕЧАНИЯ
- Используй современный Kotlin синтаксис с корутинами
- Применяй MVVM архитектуру с LiveData/StateFlow
- Все строки выноси в strings.xml с поддержкой русского языка
- Добавь логирование всех важных событий
- Используй Material Design 3 компоненты
- Обеспечь backward compatibility с API 24
- Добавь комментарии к сложной логике WebRTC и Camera2
**Этот промпт содержит все необходимые технические детали для создания полнофункционального Android приложения GodEye Signal Center!**

134
docs/WEBRTC_FIXES_GUIDE.md Normal file
View File

@@ -0,0 +1,134 @@
# 🛠️ Исправления WebRTC проблем - Руководство по тестированию
## ✅ Что было исправлено
### 1. **Событие регистрации мобильного клиента**
-**Было**: `register:android` (неправильно)
-**Стало**: `register:mobile_web` (правильно)
### 2. **Добавлена полная поддержка WebRTC**
- Добавлен класс `RTCPeerConnection`
- Обработчики WebRTC событий: `webrtc:offer`, `webrtc:answer`, `webrtc:ice-candidate`
- Правильная обработка ICE candidates
- Обработка состояний соединения
### 3. **Улучшена диагностика ошибок**
- Подробные логи WebRTC процесса
- Отслеживание состояний соединения
- Более информативные сообщения об ошибках
## 🧪 Как протестировать исправления
### Шаг 1: Перезапустить сервер (✅ Выполнено)
```bash
cd /home/data/god_eye/backend
node src/server.js
# Сервер работает на http://localhost:3001
```
### Шаг 2: Тестирование с мобильного телефона
1. **Откройте браузер на телефоне**
2. **Перейдите**: `http://[IP_СЕРВЕРА]:3001/mobile`
3. **Проверьте регистрацию**: В логах должно появиться "Mobile web client registered"
4. **Включите камеру**: Нажмите кнопку 📷
5. **Проверьте превью**: Видео должно отображаться
### Шаг 3: Тестирование с Desktop Operator
1. **Запустите Desktop Operator приложение**
2. **Подключитесь к серверу**: `ws://localhost:3001`
3. **Найдите мобильное устройство** в списке устройств
4. **Отправьте запрос** на доступ к камере
### Шаг 4: Полный цикл WebRTC
1. **На телефоне**: Принмите запрос (✅ Принять)
2. **Проверьте логи** в браузере телефона (F12 -> Console)
3. **В Desktop Operator**: Должно появиться видео с телефона
## 📊 Что проверить в логах
### Логи сервера (терминал):
```
📱 Mobile web client connected: [ID]
🌐 Mobile web event: register:mobile_web
Mobile web client registered: [device-id]
```
### Логи мобильного браузера (F12 -> Console):
```
📱 Устройство зарегистрировано
📞 Получен WebRTC offer
📝 Remote description установлен
✅ WebRTC answer отправлен
🧊 ICE candidate добавлен
🔗 WebRTC состояние: connected
```
### Логи Desktop Operator:
```
Session created: [session-id]
WebRTC offer sent
WebRTC answer received
ICE candidate received
Video stream connected
```
## 🚨 Возможные проблемы и решения
### Проблема: "Ошибка камеры: undefined"
**Решение**:
- Проверьте разрешения камеры в браузере
- Попробуйте обновить страницу
- Убедитесь что камера не занята другими приложениями
### Проблема: WebRTC соединение не устанавливается
**Решение**:
- Проверьте что оба устройства в одной сети
- Посмотрите логи на предмет ошибок ICE
- Попробуйте другой браузер
### Проблема: Видео не отображается в операторе
**Решение**:
- Проверьте что мобильный клиент правильно отправляет видео поток
- Убедитесь что Desktop Operator правильно обрабатывает WebRTC
## 🔧 Команды для отладки
### Проверить статус сервера:
```bash
curl http://localhost:3001/api/status
```
### Проверить подключенные устройства:
```bash
curl http://localhost:3001/api/devices
```
### Просмотр логов сервера в реальном времени:
```bash
tail -f /home/data/god_eye/backend/god-eye.log
```
## 📱 Узнать IP для доступа с телефона
### Linux:
```bash
hostname -I | awk '{print $1}'
```
### Или:
```bash
ip addr show | grep "inet.*192\|inet.*10\." | head -1
```
## ✨ Ожидаемый результат
После всех исправлений должно работать:
1. ✅ Мобильный браузер подключается как `mobile_web` устройство
2. ✅ Desktop Operator видит мобильное устройство в списке
3. ✅ WebRTC соединение устанавливается успешно
4. ✅ Видео с телефона транслируется в Desktop Operator
5. ✅ Переключение камер работает корректно
---
**🎯 Основная цель**: Добиться стабильной работы видеопотока между мобильным браузером и desktop оператором через WebRTC.