# 🔌 Координация WebSocket между вкладками браузера

## 📋 Описание проблемы

При открытии нескольких вкладок сайта каждая вкладка создает свое WebSocket соединение. Это приводит к:
- Дублированию сообщений
- Конфликтам при обработке событий (например, подпись пакетов)
- Лишней нагрузке на сервер
- Неожиданному поведению в UI

## ✅ Решение

Реализована система координации между вкладками, которая гарантирует, что **только одна вкладка** слушает WebSocket соединение для каждой группы пользователей.

## 🎯 Область применения

**WebSocket работает на страницах мониторинга И при наличии панели мониторинга фонового процесса:**
- `/cab/monitor/` - основная страница мониторинга
- `/cab/monitor/pl/` - страница подписи пакетов
- **Любая страница с панелью мониторинга фонового процесса** - автоматически подключает WebSocket при появлении панели

На всех остальных страницах WebSocket **не инициализируется** и не работает.

---

## 🏗️ Архитектура

### Компоненты системы:

1. **Диспетчер подключений** (`script_socket_connection.js`) - управляет подключением к WebSocket, координацией между вкладками, heartbeat
2. **Диспетчер операций для мониторинга** (`script_socket_event.js`) - обрабатывает все типы сообщений WebSocket на страницах мониторинга
3. **Диспетчер операций для фоновых процессов** (`script_socket_background_process.js`) - обрабатывает только обновления мониторинга фонового процесса
4. **localStorage** - хранит информацию об активной вкладке
5. **BroadcastChannel API** - мгновенное уведомление между вкладками
6. **Heartbeat механизм** - подтверждение активности
7. **Автоматическая смена** - переподключение при освобождении слота
8. **Приоритет страниц мониторинга** - страницы `/cab/monitor/` и `/cab/monitor/pl/` имеют приоритет
9. **Приоритет страниц с панелью фонового процесса** - страницы с панелью мониторинга фонового процесса всегда могут подключиться к WebSocket

---

## 🔑 Ключевые переменные

### Проверка страницы мониторинга (в начале):

```javascript
var is_monitor_page = page_url.includes("/cab/monitor/") || page_url.includes("/cab/monitor/pl/");
// Флаг, является ли текущая вкладка страницей мониторинга

if (!is_monitor_page) {
    return; // WebSocket не инициализируется на других страницах
}
```

### Глобальные переменные (инициализируются только на страницах мониторинга):

```javascript
var tabId = 'ws_tab_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
// Уникальный ID каждой вкладки

var isActiveTab = false;
// Флаг активности текущей вкладки

var wsLockKey = 'ws_active_tab_' + settings.group;
// Ключ localStorage для хранения ID активной вкладки

var wsLockTimestampKey = 'ws_active_tab_timestamp_' + settings.group;
// Ключ localStorage для хранения времени последнего heartbeat

var wsLockIsMonitorKey = 'ws_active_tab_is_monitor_' + settings.group;
// Ключ localStorage для хранения флага, является ли активная вкладка страницей мониторинга

var broadcastChannel = null;
// BroadcastChannel для общения между вкладками

var heartbeatInterval = null;
// Интервал heartbeat (обновление активности)

var lockCheckInterval = null;
// Интервал проверки освобождения слота
```

---

## 🔄 Основные функции

### 1. `tryBecomeActive()`

**Назначение:** Попытка стать активной вкладкой для WebSocket

**Логика:**
```
1. Проверяет localStorage на наличие активной вкладки
2. Если слот свободен (нет записи или timestamp старше 5 сек):
   - Захватывает слот (сохраняет tabId)
   - Обновляет timestamp
   - Сохраняет флаг мониторинга (is_monitor_page)
   - Уведомляет через BroadcastChannel
   - Возвращает true
3. Если мы уже активны:
   - Обновляет timestamp
   - Обновляет флаг мониторинга
   - Возвращает true
4. Если другая вкладка активна:
   - Проверяет приоритет страницы мониторинга:
     * Если текущая - мониторинг, активная - нет → вытесняем (приоритет!)
     * Если текущая - не мониторинг, активная - мониторинг → возвращает false
     * Если обе одинаковые → возвращает false (обычная логика)
```

**Ключевые проверки:**
- Нет активной вкладки → слот свободен
- Timestamp старше 5 секунд → вкладка "умерла", слот свободен
- Timestamp свежий → вкладка активна, слот занят
- **Приоритет мониторинга:** страницы `/cab/monitor/` и `/cab/monitor/pl/` всегда вытесняют не-мониторинг страницы

---

### 2. `releaseConnection()`

**Назначение:** Освобождение WebSocket соединения и слота

**Действия:**
1. Закрывает WebSocket соединение (если открыто)
2. Останавливает heartbeat
3. Очищает localStorage (только если мы владельцы слота)
4. Сбрасывает флаг `isActiveTab`
5. **Запускает проверку слота** (`startLockCheck`) для попытки снова стать активной

**Вызывается при:**
- Закрытии вкладки (`beforeunload`)
- Получении уведомления о смене активной вкладки
- Вручную при необходимости

**Важно:** После освобождения соединения вкладка автоматически начинает проверять возможность снова стать активной

---

### 3. `startHeartbeat()`

**Назначение:** Подтверждение активности активной вкладки

**Механизм:**
- Запускается каждые **2 секунды**
- Обновляет `wsLockTimestampKey` в localStorage
- Позволяет другим вкладкам определить, жива ли активная вкладка

**Важно:** Heartbeat работает только у активной вкладки

---

### 4. `startLockCheck()`

**Назначение:** Периодическая проверка освобождения слота неактивными вкладками

**Механизм:**
- Запускается каждые **3 секунды** у неактивных вкладок
- Проверяет, не освободился ли слот
- Если слот свободен → пытается стать активной

**Защита от гонок:**
- `tryBecomeActive()` атомарно захватывает слот
- Только одна вкладка успеет захватить слот

---

### 5. `connect_ws()`

**Назначение:** Установка WebSocket соединения

**Изменения:**
- Добавлена проверка `tryBecomeActive()` перед подключением
- Подключается только если вкладка активна
- Запускает heartbeat после успешного подключения

**Проверка активности:**
```javascript
if (!isActiveTab) {
    console.log('Невозможно подключить WebSocket: другая вкладка активна');
    return;
}
```

---

### 6. `notifyUser(obj)`

**Назначение:** Обработка WebSocket сообщений

**Изменения:**
- Добавлена проверка активности перед обработкой
- Неактивные вкладки игнорируют все сообщения

```javascript
if (!isActiveTab) {
    console.log('notifyUser: вкладка не активна, игнорируем сообщение');
    return;
}
```

---

## 📡 BroadcastChannel API

### Инициализация:

```javascript
broadcastChannel = new BroadcastChannel('ws_tab_coordination_' + settings.group);
```

**Канал уникален для каждой группы**, что позволяет:
- Разным группам иметь разные активные вкладки
- Не мешать друг другу при координации

### Уведомления:

**При активации вкладки:**
```javascript
broadcastChannel.postMessage({
    type: 'tab_activated',
    tabId: tabId,
    isMonitor: is_monitor_page  // Флаг мониторинга
});
```

**При получении уведомления:**
```javascript
broadcastChannel.onmessage = function(event) {
    if (event.data.type === 'tab_activated' && event.data.tabId !== tabId) {
        var otherIsMonitor = event.data.isMonitor === true;
        
        // Если другая вкладка - мониторинг, а мы нет, освобождаем
        if (otherIsMonitor && !is_monitor_page) {
            releaseConnection();
        }
        // Если мы мониторинг, а другая нет - пытаемся захватить (приоритет)
        else if (!otherIsMonitor && is_monitor_page) {
            tryBecomeActive(); // Попытка вытеснить не-мониторинг
        }
        // Обе одинаковые - обычная логика
        else {
            releaseConnection();
        }
    }
};
```

---

## ⏱️ Таймауты и интервалы

| Механизм | Интервал | Назначение |
|----------|----------|------------|
| **Heartbeat** | 2 секунды | Обновление timestamp активности |
| **Lock Check** | 3 секунды | Проверка освобождения слота |
| **Timeout** | 5 секунд | Максимальное время без heartbeat |

**Логика:**
- Если heartbeat не обновлялся > 5 секунд → вкладка считается "мертвой"
- Слот автоматически освобождается для других вкладок

---

## 🎬 Сценарии работы

### Сценарий 1: Открытие первой вкладки

```
1. Вкладка 1 загружается
2. Вызывается tryBecomeActive()
3. localStorage пуст → слот свободен ✅
4. Вкладка 1 захватывает слот
5. connect_ws() → WebSocket подключается
6. startHeartbeat() → начинается подтверждение активности
7. WebSocket активен ✅
```

---

### Сценарий 2: Открытие второй вкладки

```
1. Вкладка 2 загружается
2. Вызывается tryBecomeActive()
3. localStorage содержит tabId вкладки 1
4. Timestamp свежий (< 5 сек) → слот занят ❌
5. Вкладка 2 не подключается к WebSocket
6. startLockCheck() → начинает проверку освобождения
7. WebSocket не активен, ждет освобождения ⏳
```

---

### Сценарий 3: Закрытие активной вкладки

```
1. Вкладка 1 закрывается
2. beforeunload → releaseConnection()
3. WebSocket закрывается
4. localStorage очищается (wsLockKey, wsLockTimestampKey)
5. BroadcastChannel уведомляет (не срабатывает, вкладка закрыта)

Вкладка 2 (через 3 сек):
6. lockCheckInterval проверяет слот
7. tryBecomeActive() → слот свободен ✅
8. Вкладка 2 захватывает слот
9. connect_ws() → WebSocket подключается
10. WebSocket активен ✅
```

---

### Сценарий 4: Зависание активной вкладки

```
1. Вкладка 1 зависает/крэшится
2. Heartbeat перестает обновляться
3. Timestamp не обновляется > 5 секунд

Вкладка 2 (через 3 сек):
4. lockCheckInterval проверяет слот
5. tryBecomeActive():
   - Timestamp старше 5 сек → вкладка 1 "мертва"
   - Слот считается свободным ✅
6. Вкладка 2 захватывает слот
7. connect_ws() → WebSocket подключается
8. WebSocket активен ✅
```

---

### Сценарий 5: Переключение между вкладками

```
1. Вкладка 1 активна, WebSocket работает
2. Пользователь переключается на вкладку 2
3. Вкладка 2 получает visibilitychange (visible)
4. tryBecomeActive():
   - Вкладка 1 активна (timestamp свежий)
   - Возвращает false ❌
5. Вкладка 2 остается неактивной

Если вкладка 1 закрыта:
6. Вкладка 2 через 3 сек проверяет слот
7. Слот свободен → вкладка 2 становится активной ✅
```

---

### Сценарий 6: Открытие обычной страницы (не мониторинг)

```
1. Вкладка 1 (/cab/monitor/pl/) активна, WebSocket работает
2. Пользователь открывает вкладку 2 (обычная страница, например /cab/profile/)
3. Вкладка 2 проверяет is_monitor_page и hasBackgroundProcessPanel()
4. Оба флага = false → ранний выход ❌
5. WebSocket НЕ инициализируется на вкладке 2
6. Вкладка 1 продолжает работать как и прежде ✅
```

---

### Сценарий 8: Запуск фонового процесса на обычной странице

```
1. Вкладка 1 (/cab/setting/update/) - обычная страница, WebSocket не инициализирован
2. Пользователь запускает фоновый процесс (например, проверку настроек)
3. Появляется панель мониторинга фонового процесса (.panel_background_process)
4. Система обнаруживает панель через 1 секунду
5. WebSocket автоматически инициализируется ✅
6. Вкладка 1 пытается стать активной для WebSocket
7. Если другая вкладка мониторинга активна - вкладка 1 ждет освобождения слота
8. Если слот свободен - вкладка 1 становится активной и подключается к WebSocket ✅
9. Мониторинг фонового процесса работает через WebSocket ✅

При завершении процесса:
10. Панель мониторинга исчезает
11. Система обнаруживает исчезновение панели через 3 секунды
12. WebSocket соединение автоматически освобождается ✅
```

---

### Сценарий 7: Две вкладки мониторинга

```
1. Вкладка 1 (/cab/monitor/) активна, WebSocket работает
2. Пользователь открывает вкладку 2 (/cab/monitor/pl/)
3. Вкладка 2 вызывает tryBecomeActive():
   - Вкладка 1 активна (timestamp свежий)
   - Обе вкладки - мониторинг (равнозначны)
   - Возвращает false ❌
4. Вкладка 2 остается неактивной, запускает lockCheck
5. Работает обычная логика: первая захваченная вкладка остается активной

Если вкладка 1 закрывается:
6. Вкладка 2 через 3 сек проверяет слот
7. Слот свободен → вкладка 2 становится активной ✅
```

---

## 🎯 Ограничение работы WebSocket

### Описание

**WebSocket работает на страницах мониторинга И при наличии панели мониторинга фонового процесса:**
- `/cab/monitor/` - основная страница мониторинга
- `/cab/monitor/pl/` - страница подписи пакетов
- **Любая страница с панелью мониторинга фонового процесса** - автоматически подключает WebSocket при появлении панели

На всех остальных страницах WebSocket **не инициализируется** и не создается.

### Реализация:

**Проверка в начале файла:**
```javascript
var is_monitor_page = page_url.includes("/cab/monitor/") || page_url.includes("/cab/monitor/pl/");

// Функция проверки наличия панели мониторинга фонового процесса
function hasBackgroundProcessPanel() {
    return $j('.panel_background_process').length > 0 || 
           $j('.panel_log_uplod').length > 0 || 
           $j('div[id^="process_status_"]').length > 0;
}

var has_background_panel = hasBackgroundProcessPanel();
var should_init_ws = is_monitor_page || has_background_panel;

if (!should_init_ws) {
    console.log('WebSocket отключен: это не страница мониторинга и нет панели мониторинга фонового процесса');
    // Запускаем периодическую проверку появления панели
    // ...
    return; // Ранний выход, WebSocket не инициализируется пока
}
```

**Автоматическое подключение при появлении панели:**
- Если панель мониторинга фонового процесса появляется динамически (через AJAX), WebSocket автоматически инициализируется
- Проверка наличия панели выполняется каждую секунду
- При исчезновении панели WebSocket соединение автоматически освобождается

### Логика между вкладками мониторинга:

Между страницами мониторинга (`/cab/monitor/` и `/cab/monitor/pl/`) работает обычная логика координации:
- Первая открытая вкладка захватывает WebSocket
- При закрытии активной вкладки другая вкладка мониторинга автоматически подхватывает соединение
- Нет приоритета между `/cab/monitor/` и `/cab/monitor/pl/` - они равнозначны

### Логика для страниц с панелью мониторинга фонового процесса:

- Страницы с панелью мониторинга фонового процесса имеют **низший приоритет** по сравнению со страницами мониторинга
- Если активна страница мониторинга, страница с панелью фонового процесса не может вытеснить её
- Если активна страница с панелью фонового процесса, а открывается страница мониторинга - страница мониторинга получает приоритет и вытесняет страницу с панелью
- Между страницами с панелью фонового процесса работает обычная логика координации (первая захватывает слот)

### Преимущества:

✅ **Четкое разграничение** - WebSocket только там, где он нужен
✅ **Нет лишней нагрузки** - другие страницы не создают WebSocket соединения
✅ **Простая логика** - все страницы мониторинга равнозначны
✅ **Автоматическое переключение** - между вкладками мониторинга

---

## 🔒 Изоляция по группам

### Ключи localStorage:

```javascript
'ws_active_tab_' + group_id        // ID активной вкладки
'ws_active_tab_timestamp_' + group_id  // Timestamp активности
'ws_active_tab_is_monitor_' + group_id  // Флаг: является ли активная вкладка страницей мониторинга ('true'/'false')
```

### BroadcastChannel:

```javascript
'ws_tab_coordination_' + group_id
```

**Преимущества:**
- Пользователи разных групп не мешают друг другу
- Каждая группа имеет свой независимый слот
- Можно открыть активную вкладку для каждой группы

---

## 📊 Диаграмма состояний

```
┌─────────────────────────────────────────────────────────┐
│                    НОВАЯ ВКЛАДКА                        │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
         ┌───────────────────────┐
         │ tryBecomeActive()     │
         └───────────┬───────────┘
                     │
        ┌────────────┴────────────┐
        │                         │
        ▼                         ▼
   ┌─────────┐             ┌──────────┐
   │  TRUE   │             │  FALSE   │
   └────┬────┘             └────┬─────┘
        │                       │
        │                       ▼
        │              ┌─────────────────┐
        │              │  НЕАКТИВНАЯ     │
        │              │  startLockCheck │
        │              │  (каждые 3 сек) │
        │              └────────┬────────┘
        │                       │
        │                       │ (слот свободен)
        │                       ▼
        │              ┌─────────────────┐
        │              │ tryBecomeActive │
        │              └────────┬────────┘
        │                       │
        └───────────────────────┼───────────┐
                                │           │
                                ▼           ▼
                    ┌───────────────────────────┐
                    │      АКТИВНАЯ             │
                    │  connect_ws()             │
                    │  startHeartbeat()         │
                    │  (каждые 2 сек)           │
                    └───────────┬───────────────┘
                                │
                    ┌───────────┴───────────┐
                    │                       │
                    ▼                       ▼
        ┌──────────────────┐   ┌──────────────────┐
        │  beforeunload    │   │  Другая вкладка  │
        │  releaseConn()   │   │  активирована    │
        └──────────────────┘   │  releaseConn()   │
                               └──────────────────┘
```

---

## 🛡️ Защита от гонок (Race Conditions)

### Проблема:
Несколько вкладок могут одновременно попытаться захватить свободный слот.

### Решение:

1. **Атомарная проверка и захват:**
   ```javascript
   var activeTabId = localStorage.getItem(wsLockKey);
   if (!activeTabId || timestamp_expired) {
       localStorage.setItem(wsLockKey, tabId);  // Атомарно
   }
   ```

2. **Повторная проверка после захвата:**
   ```javascript
   var currentActive = localStorage.getItem(wsLockKey);
   if (currentActive === tabId) {
       // Мы успешно захватили слот
   }
   ```

3. **BroadcastChannel уведомление:**
   - Мгновенно информирует другие вкладки
   - Они сразу освобождают свои попытки

---

## 🔍 Логирование и отладка

### Сообщения в консоли:

**При активации:**
```
Эта вкладка стала активной для WebSocket. Tab ID: ws_tab_1234567890_abc123, Мониторинг: true
```

**При блокировке:**
```
Другая вкладка активна для WebSocket. Активная: ws_tab_1234567890_xyz789, Мониторинг: false
WebSocket не будет подключен в этой вкладке. Другая вкладка уже активна.
```

**При приоритете мониторинга:**
```
Вкладка мониторинга получает приоритет. Вытесняем вкладку: ws_tab_1234567890_xyz789
Вкладка мониторинга стала активной. Tab ID: ws_tab_1234567890_abc123
Вкладка мониторинга стала активной: ws_tab_1234567890_abc123. Освобождаем слот.
Вкладка мониторинга получает приоритет. Пытаемся захватить слот.
```

**При освобождении:**
```
Другая вкладка стала активной: ws_tab_1234567890_xyz789
Освобождение WebSocket соединения
```

**При получении сообщения неактивной вкладкой:**
```
Получено сообщение, но вкладка не активна. Игнорируем.
notifyUser: вкладка не активна, игнорируем сообщение
```

---

## ⚙️ Настройка таймаутов

### Текущие значения:

```javascript
HEARTBEAT_INTERVAL = 2000;    // 2 секунды - обновление активности
LOCK_CHECK_INTERVAL = 3000;   // 3 секунды - проверка освобождения
TIMEOUT_THRESHOLD = 5000;     // 5 секунд - таймаут неактивности
RECONNECT_DELAY = 1000;       // 1 секунда - задержка переподключения
```

### Рекомендации:

- **Heartbeat < Timeout** - heartbeat должен быть меньше таймаута
- **Lock Check < Timeout** - проверка должна быть чаще таймаута
- **Reconnect Delay** - достаточно 1 секунды для стабильности

---

## 🌐 Совместимость браузеров

### BroadcastChannel API:
- ✅ Chrome 54+
- ✅ Firefox 38+
- ✅ Safari 15.4+
- ✅ Edge 79+
- ⚠️ IE11 - не поддерживается (fallback через localStorage)

### Fallback механизм:

Если BroadcastChannel недоступен:
- Координация работает только через localStorage
- Смена активной вкладки происходит с задержкой (до 3 секунд)
- Функциональность сохраняется

---

## 🐛 Возможные проблемы и решения

### Проблема 1: Две вкладки одновременно активны

**Причина:** Редкая гонка при одновременной проверке

**Решение:** 
- Повторная проверка в `connect_ws()`
- Игнорирование сообщений неактивными вкладками
- Heartbeat подтверждает реальную активность

---

### Проблема 2: Вкладка не освобождает слот при зависании

**Решение:**
- Timeout 5 секунд определяет "мертвые" вкладки
- Другие вкладки автоматически захватывают слот

---

### Проблема 3: WebSocket не поднимается в других вкладках после закрытия активной

**Причина:** Неактивные вкладки останавливали проверку слота (`lockCheckInterval`) при получении уведомления о смене активной вкладки

**Решение:**
- `releaseConnection()` больше не останавливает `lockCheckInterval`
- После освобождения соединения вкладка автоматически запускает `startLockCheck()`
- Неактивные вкладки всегда имеют запущенную проверку слота
- Это гарантирует, что при закрытии активной вкладки другая вкладка подхватит соединение через 3 секунды

---

### Проблема 4: Частые переключения между вкладками

**Текущее поведение:**
- Активная вкладка остается активной даже при потере фокуса
- Переключение происходит только при закрытии активной вкладки

**Возможное улучшение:**
Можно добавить переключение при потере фокуса, но это может быть избыточно.

---

## 📝 Изменения в коде

### Новая архитектура (разделение на диспетчеры):

**Созданные файлы:**
1. **`script_socket_connection.js`** - Диспетчер подключений
   - Управляет подключением к WebSocket
   - Координация между вкладками
   - Heartbeat механизм
   - Автоматическая смена активной вкладки
   - Приоритет страниц мониторинга
   - **Приоритет страниц с панелью фонового процесса** - страницы с панелью всегда могут подключиться

2. **`script_socket_background_process.js`** - Диспетчер операций для фоновых процессов
   - Обрабатывает только обновления мониторинга фонового процесса (`update_content`)
   - Упрощенная версия обработчика сообщений

**Модифицированные файлы:**
1. **`script_socket_event.js`** - Диспетчер операций для мониторинга
   - Обрабатывает все типы сообщений WebSocket
   - Использует диспетчер подключений для управления подключением
   - Работает только на страницах мониторинга

2. **`c_page_cabinet.php`** - Обновлена логика подключения скриптов
   - Подключает диспетчер подключений для всех страниц
   - Подключает полный диспетчер операций для страниц мониторинга
   - Подключает упрощенный диспетчер операций для обычных страниц

**Модифицировано:**
- `connect_ws()` - проверка активности
- `ws.onmessage` - игнорирование неактивными вкладками
- `notifyUser()` - проверка активности
- `ws.onclose` - переподключение только активной вкладки
- `tryBecomeActive()` - добавлена логика приоритета мониторинга
- `releaseConnection()` - очистка флага мониторинга, **запуск проверки слота после освобождения**
- `startHeartbeat()` - обновление флага мониторинга
- `broadcastChannel.onmessage` - обработка приоритета мониторинга, **гарантия запуска проверки слота у неактивных вкладок**
- `visibilitychange` - учет приоритета при смене видимости

**Исправлено и изменено (Dec 2025):**
- ✅ **WebSocket теперь работает на страницах мониторинга** (`/cab/monitor/`, `/cab/monitor/pl/`)
- ✅ **WebSocket автоматически подключается при появлении панели мониторинга фонового процесса**
- ✅ **WebSocket автоматически освобождается при исчезновении панели мониторинга фонового процесса**
- ✅ На всех остальных страницах WebSocket не инициализируется (ранний выход из функции)
- ✅ Проблема, когда WebSocket не поднимался в других вкладках после закрытия активной
- ✅ Теперь все неактивные вкладки всегда имеют запущенную проверку слота
- ✅ После `releaseConnection()` вкладка автоматически запускает `startLockCheck()`

---

## ✅ Преимущества решения

1. ✅ **Работает только где нужно** - WebSocket инициализируется ТОЛЬКО на страницах мониторинга
2. ✅ **Одно соединение** - только одна вкладка мониторинга слушает WebSocket
3. ✅ **Автоматическая смена** - при закрытии активной вкладки другая подхватывает соединение
4. ✅ **Защита от зависаний** - timeout определяет неактивные вкладки
5. ✅ **Изоляция по группам** - каждая группа независима
6. ✅ **Минимальная задержка** - BroadcastChannel для мгновенных уведомлений
7. ✅ **Совместимость** - работает даже без BroadcastChannel
8. ✅ **Безопасность** - защита от гонок и конфликтов
9. ✅ **Нет лишней нагрузки** - обычные страницы не создают WebSocket соединения
10. ✅ **Простая логика** - все страницы мониторинга равнозначны

---

## 🔮 Возможные улучшения

1. **Визуальная индикация:**
   - Показывать статус "активная/ожидание" в UI
   - Информация о количестве открытых вкладок

2. **Настройки:**
   - Позволить пользователю выбирать активную вкладку вручную
   - Настройка таймаутов через параметры

3. **Аналитика:**
   - Логирование переключений между вкладками
   - Метрики использования WebSocket

4. **Улучшенное переключение:**
   - Переключение при потере фокуса
   - Приоритет видимой вкладки

---

## 📚 Связанные файлы

- `esmo/src/class_v.esmo/ext/aspmo_monitor/script_socket_event.js` - основная реализация
- `esmo/src/class_v.esmo/ext/aspmo_monitor/script_mon_ws.js` - обработка событий мониторинга
- `esmo/src/class_v.esmo/ext/aspmo_monitor/c_monitor_pl.php` - страница подписи пакетов

---

**Дата создания:** 2024
**Версия:** 1.0
**Автор:** Система координации WebSocket между вкладками

