main commit
This commit is contained in:
69
docs/CACHE_CLEAR_INSTRUCTIONS.md
Normal file
69
docs/CACHE_CLEAR_INSTRUCTIONS.md
Normal 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
|
||||
- ✅ **Видно в операторе**: Смена видео при переключении камеры
|
||||
|
||||
---
|
||||
|
||||
**🚨 КРИТИЧЕСКИ ВАЖНО**: Обязательно очистите кэш браузера на телефоне, иначе исправления не будут работать!
|
||||
162
docs/COMPLETE_UI_TESTING_GUIDE.md
Normal file
162
docs/COMPLETE_UI_TESTING_GUIDE.md
Normal 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
316
docs/CONNECTION_MANAGER.md
Normal 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` (клиентские методы)
|
||||
72
docs/DESKTOP_APP_TESTING_GUIDE.md
Normal file
72
docs/DESKTOP_APP_TESTING_GUIDE.md
Normal 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**: выйти из полноэкранного режима
|
||||
112
docs/FIXED_SESSION_TESTING_GUIDE.md
Normal file
112
docs/FIXED_SESSION_TESTING_GUIDE.md
Normal 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. **Правильная синхронизация** - состояние между сервером и клиентом всегда синхронизировано
|
||||
|
||||
Теперь система сессий работает полноценно и позволяет эффективно управлять множественными подключениями!
|
||||
116
docs/IMPROVED_UI_TESTING_GUIDE.md
Normal file
116
docs/IMPROVED_UI_TESTING_GUIDE.md
Normal 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
107
docs/MOBILE_TEST_GUIDE.md
Normal 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 соединения
|
||||
288
docs/OPERATOR_TO_ANDROID_CONNECTION.md
Normal file
288
docs/OPERATOR_TO_ANDROID_CONNECTION.md
Normal 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 видеопотоком!
|
||||
484
docs/SERVER_TO_ANDROID_PROTOCOL.md
Normal file
484
docs/SERVER_TO_ANDROID_PROTOCOL.md
Normal 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 устройств с полным контролем пользователя над разрешениями.
|
||||
479
docs/TECHNICAL_SPECIFICATION_ANDROID.md
Normal file
479
docs/TECHNICAL_SPECIFICATION_ANDROID.md
Normal 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
134
docs/WEBRTC_FIXES_GUIDE.md
Normal 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.
|
||||
Reference in New Issue
Block a user