connection fixes
This commit is contained in:
0
docs/ANDROID_APP_FIX.md
Normal file
0
docs/ANDROID_APP_FIX.md
Normal file
0
docs/ANDROID_COMPATIBILITY_FIX.md
Normal file
0
docs/ANDROID_COMPATIBILITY_FIX.md
Normal file
0
docs/ANDROID_DEBUG_REPORT.md
Normal file
0
docs/ANDROID_DEBUG_REPORT.md
Normal file
0
docs/ARCHITECTURE_DIAGRAM.md
Normal file
0
docs/ARCHITECTURE_DIAGRAM.md
Normal file
0
docs/BUGFIX_REPORT.md
Normal file
0
docs/BUGFIX_REPORT.md
Normal file
0
docs/CAMERA_SWITCH_FIX.md
Normal file
0
docs/CAMERA_SWITCH_FIX.md
Normal file
@@ -1,316 +0,0 @@
|
||||
# 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` (клиентские методы)
|
||||
0
docs/CONNECTION_PROCESS.md
Normal file
0
docs/CONNECTION_PROCESS.md
Normal file
0
docs/DEPLOYMENT_GUIDE.md
Normal file
0
docs/DEPLOYMENT_GUIDE.md
Normal file
0
docs/DESKTOP_OPERATOR_FIX.md
Normal file
0
docs/DESKTOP_OPERATOR_FIX.md
Normal file
0
docs/DEVICE_REGISTRATION_FIX.md
Normal file
0
docs/DEVICE_REGISTRATION_FIX.md
Normal file
0
docs/NO_SERVER_ON_PHONE.md
Normal file
0
docs/NO_SERVER_ON_PHONE.md
Normal file
@@ -1,288 +0,0 @@
|
||||
# Руководство: Подключение 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 видеопотоком!
|
||||
0
docs/SERVER_REQUIREMENTS.md
Normal file
0
docs/SERVER_REQUIREMENTS.md
Normal file
@@ -1,484 +0,0 @@
|
||||
# Протокол запросов от сервера к 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 устройств с полным контролем пользователя над разрешениями.
|
||||
0
docs/SYSTEM_STATUS_REPORT.md
Normal file
0
docs/SYSTEM_STATUS_REPORT.md
Normal file
0
docs/UI_IMPROVEMENTS_SUMMARY.md
Normal file
0
docs/UI_IMPROVEMENTS_SUMMARY.md
Normal file
0
docs/UNIVERSAL_VIDEO_STREAMING.md
Normal file
0
docs/UNIVERSAL_VIDEO_STREAMING.md
Normal file
0
docs/WEBRTC_FIXES_SUMMARY.md
Normal file
0
docs/WEBRTC_FIXES_SUMMARY.md
Normal file
0
docs/ai_android_fix_prompt.md
Normal file
0
docs/ai_android_fix_prompt.md
Normal file
0
docs/server-calculator.html
Normal file
0
docs/server-calculator.html
Normal file
Reference in New Issue
Block a user