Files
vue-pult/docs/CONFIG_LOGIC_ANALYSIS.md
2025-10-07 10:18:02 +03:00

23 KiB
Raw Blame History

🔍 Анализ логики настроек ESP - Детальная проверка

Проверенные компоненты

1. Frontend (Client)

📄 client/src/composables/config/useConfigESP.js

Статус: Логика корректна

Проверенные аспекты:

  • Валидация параметров перед отправкой (строки 93-113)
  • Загрузка настроек через cfg-esp-get (строки 118-143)
  • Сохранение параметра через cfg-esp (строки 148-190)
  • Откат значения при ошибке (строки 176, 183)
  • Обработка ответа error от сервера (строки 174-177)
  • Отслеживание изменений через hasChanges (строки 79-88)

Ключевые моменты:

// Валидация
const validation = validateParam(paramName, config.value[paramName])
if (!validation.valid) {
  error.value = validation.error
  return { success: false, error: validation.error }
}

// Откат при ошибке
if (response.do === 'error') {
  config.value[paramName] = originalConfig.value[paramName]
  throw new Error(response.message || 'Ошибка сохранения')
}

📄 client/src/pages/ConfigPage.vue

Статус: Логика корректна (исправлена)

Проверенные аспекты:

  • Проверка прав доступа: user.taccess === 'technics' или 'admins' (строка 163)
  • Загрузка настроек при монтировании (строки 188-192)
  • Визуальная индикация изменений
  • Предупреждение при выходе с несохранёнными изменениями (строки 178-186)

Исправления:

// БЫЛО (неправильно):
return role === 'technic' || role === 'admin' || user.group === 3

// СТАЛО (правильно):
return user.taccess === 'technics' || user.taccess === 'admins' || user.group === 3

📄 client/src/composables/useWebSocket.js

Статус: Логика корректна (добавлена обработка)

Добавленная обработка (строки 71-80):

// Обрабатываем ответы настроек ESP и ошибки через promise
if (data.do === 'cfg-esp-get' || data.do === 'cfg-esp' || data.do === 'error') {
  const promiseKeys = Object.keys(pendingPromises)
  if (promiseKeys.length > 0) {
    const promiseId = promiseKeys[promiseKeys.length - 1]
    pendingPromises[promiseId].resolve(data)
    delete pendingPromises[promiseId]
    return
  }
}

Зачем это нужно:

  • Без этого кода ответы cfg-esp-get, cfg-esp и error не попадали бы в promise
  • Promise висел бы в pending состоянии до timeout
  • UI не получал бы ответ от сервера

2. Backend (Server)

📄 server/ws.js

Статус: Логика корректна (добавлена поддержка mock режима)

Обработчик cfg-esp-get (строки 952-972):

case "cfg-esp-get":
  try {
    const espConfig = JSON.parse(await fs.readFile(path.join(__dirname, 'data/esp.ini'), 'utf8'));
    sendToClient(wsClient, {
      do: "cfg-esp-get",
      cfg: espConfig
    });
  } catch (error) {
    sendToClient(wsClient, {
      do: "error",
      message: "Не удалось загрузить настройки ESP",
      type: "esp_config_read_error"
    });
  }
  break;

Обработчик cfg-esp (строки 974-1038):

Ключевые исправления:

// БЫЛО (неправильно для Windows):
if (result.success) {
  await fs.writeFile(configPath, JSON.stringify(espConfig, null, 2));
  // ...
}

// СТАЛО (правильно):
const isMockMode = result.error && result.error.includes('mock mode');

if (result.success || isMockMode) {
  await fs.writeFile(configPath, JSON.stringify(espConfig, null, 2));
  // ...
  if (isMockMode) {
    console.log(`[WS CFG-ESP] ⚠️ MOCK РЕЖИМ: Параметр сохранён без отправки на ESP`);
  }
}

Зачем это нужно:

  • В Windows режиме разработки ESP не подключена (mock режим)
  • game.safeWrite() возвращает { success: false, error: 'ESP disconnected (mock mode)' }
  • Без проверки на isMockMode настройки НЕ сохранялись бы в файл
  • Теперь в mock режиме настройки сохраняются, но с предупреждением

🔄 Полный поток данных

Сценарий 1: Загрузка настроек

┌────────────────────────────────────────────────────────────┐
│ 1. ConfigPage.vue (onMounted)                              │
│    └─> loadConfig()                                        │
└────────────────────────────────────────────────────────────┘
                       ↓
┌────────────────────────────────────────────────────────────┐
│ 2. useConfigESP.js                                         │
│    └─> socket.sendMessage({ do: 'cfg-esp-get' })          │
└────────────────────────────────────────────────────────────┘
                       ↓ WebSocket
┌────────────────────────────────────────────────────────────┐
│ 3. server/ws.js:952                                        │
│    └─> Читает data/esp.ini                                │
│    └─> Отправляет {do:"cfg-esp-get", cfg:{...}}           │
└────────────────────────────────────────────────────────────┘
                       ↓ WebSocket
┌────────────────────────────────────────────────────────────┐
│ 4. useWebSocket.js:72                                      │
│    └─> Обнаруживает data.do === 'cfg-esp-get'             │
│    └─> Резолвит promise                                   │
└────────────────────────────────────────────────────────────┘
                       ↓
┌────────────────────────────────────────────────────────────┐
│ 5. useConfigESP.js:128                                     │
│    └─> config.value = { ...response.cfg }                 │
│    └─> originalConfig.value = { ...response.cfg }         │
└────────────────────────────────────────────────────────────┘
                       ↓
┌────────────────────────────────────────────────────────────┐
│ 6. ConfigPage.vue                                          │
│    └─> Отображает настройки в UI                          │
└────────────────────────────────────────────────────────────┘

Сценарий 2: Сохранение параметра (УСПЕХ)

┌────────────────────────────────────────────────────────────┐
│ 1. ConfigPage.vue                                          │
│    └─> Пользователь изменяет hz с 14000 на 15000          │
│    └─> Кликает "Применить"                                │
│    └─> saveParam('hz')                                    │
└────────────────────────────────────────────────────────────┘
                       ↓
┌────────────────────────────────────────────────────────────┐
│ 2. useConfigESP.js:148                                     │
│    └─> Валидация: 9999 ≤ 15000 ≤ 100000 ✅                │
│    └─> socket.sendMessage({                               │
│         do: 'cfg-esp',                                     │
│         cfg: 'hz',                                         │
│         value: 15000                                       │
│       })                                                   │
└────────────────────────────────────────────────────────────┘
                       ↓ WebSocket
┌────────────────────────────────────────────────────────────┐
│ 3. server/ws.js:974                                        │
│    └─> Читает текущий esp.ini                             │
│    └─> Сохраняет previousValue = 14000                    │
│    └─> Обновляет espConfig.hz = 15000                     │
│    └─> game.safeWrite("hz=15000;\n")                      │
└────────────────────────────────────────────────────────────┘
                       ↓ Serial Port
┌────────────────────────────────────────────────────────────┐
│ 4. game.js:127 (safeWrite)                                │
│    └─> Проверяет port.isOpen                              │
│    └─> Production: отправляет на ESP                      │
│    └─> Mock (Windows): return {success:false, error:...}  │
└────────────────────────────────────────────────────────────┘
                       ↓
┌────────────────────────────────────────────────────────────┐
│ 5. server/ws.js:1000                                       │
│    └─> const isMockMode = result.error?.includes('mock')  │
│    └─> if (result.success || isMockMode)                  │
│    └─> Сохраняет в esp.ini                                │
│    └─> sendToClient({do:"cfg-esp", cfg:"hz", value:15000})│
└────────────────────────────────────────────────────────────┘
                       ↓ WebSocket
┌────────────────────────────────────────────────────────────┐
│ 6. useWebSocket.js:72                                      │
│    └─> Обнаруживает data.do === 'cfg-esp'                 │
│    └─> Резолвит promise                                   │
└────────────────────────────────────────────────────────────┘
                       ↓
┌────────────────────────────────────────────────────────────┐
│ 7. useConfigESP.js:169                                     │
│    └─> originalConfig.value.hz = 15000                    │
│    └─> return { success: true }                           │
└────────────────────────────────────────────────────────────┘
                       ↓
┌────────────────────────────────────────────────────────────┐
│ 8. ConfigPage.vue                                          │
│    └─> Кнопка "Применить" исчезает                        │
│    └─> Показывается "✓ Сохранено"                         │
└────────────────────────────────────────────────────────────┘

Сценарий 3: Сохранение параметра (ОШИБКА ESP)

┌────────────────────────────────────────────────────────────┐
│ 1-3. Аналогично успешному сценарию                        │
└────────────────────────────────────────────────────────────┘
                       ↓
┌────────────────────────────────────────────────────────────┐
│ 4. game.js:127 (safeWrite)                                │
│    └─> ESP не отвечает / ошибка отправки                  │
│    └─> return { success: false, error: "timeout" }        │
└────────────────────────────────────────────────────────────┘
                       ↓
┌────────────────────────────────────────────────────────────┐
│ 5. server/ws.js:1018                                       │
│    └─> result.success === false                           │
│    └─> isMockMode === false (реальная ошибка)             │
│    └─> espConfig.hz = previousValue (откат к 14000)       │
│    └─> sendToClient({                                     │
│         do: "error",                                       │
│         message: "Ошибка настройки ESP: timeout",         │
│         type: "esp_config_failed",                        │
│         cfg: "hz"                                          │
│       })                                                   │
└────────────────────────────────────────────────────────────┘
                       ↓ WebSocket
┌────────────────────────────────────────────────────────────┐
│ 6. useWebSocket.js:72                                      │
│    └─> Обнаруживает data.do === 'error'                   │
│    └─> Резолвит promise с error                           │
└────────────────────────────────────────────────────────────┘
                       ↓
┌────────────────────────────────────────────────────────────┐
│ 7. useConfigESP.js:174                                     │
│    └─> response.do === 'error'                            │
│    └─> config.value.hz = originalConfig.value.hz (14000)  │
│    └─> throw new Error(response.message)                  │
└────────────────────────────────────────────────────────────┘
                       ↓
┌────────────────────────────────────────────────────────────┐
│ 8. useConfigESP.js:181 (catch)                            │
│    └─> config.value.hz = originalConfig.value.hz (14000)  │
│    └─> error.value = err.message                          │
│    └─> return { success: false, error: err.message }      │
└────────────────────────────────────────────────────────────┘
                       ↓
┌────────────────────────────────────────────────────────────┐
│ 9. ConfigPage.vue                                          │
│    └─> Значение откатывается к 14000                      │
│    └─> Показывается ошибка "❌ Не удалось сохранить"      │
└────────────────────────────────────────────────────────────┘

🛡️ Проверки безопасности

1. Валидация на клиенте

validateParam(paramName, value) {
  const schema = ESP_CONFIG_SCHEMA[paramName]
  const numValue = Number(value)

  if (isNaN(numValue)) {
    return { valid: false, error: 'Значение должно быть числом' }
  }

  if (numValue < schema.min || numValue > schema.max) {
    return { valid: false, error: `Значение должно быть от ${schema.min} до ${schema.max}` }
  }

  return { valid: true, error: null }
}

2. Проверка прав доступа

const hasAccess = computed(() => {
  return user.taccess === 'technics' || user.taccess === 'admins' || user.group === 3
})

3. Откат при ошибках

  • Клиент: Откат config.value к originalConfig.value
  • Сервер: Откат espConfig к previousValue
  • Файл: Сохранение только при успехе

⚠️ Найденные и исправленные проблемы

Проблема 1: Обработка WebSocket ответов

Проблема: Ответы cfg-esp-get, cfg-esp, error не обрабатывались в useWebSocket.js

Решение: Добавлена специальная обработка в onmessage (строки 71-80)


Проблема 2: Mock режим Windows

Проблема: В Windows (mock режим) настройки НЕ сохранялись

Решение: Добавлена проверка isMockMode для разрешения сохранения без ESP


Проблема 3: Проверка прав доступа

Проблема: Неправильные значения ролей (technic вместо technics)

Решение: Исправлено на user.taccess === 'technics' и 'admins'


Итоговая оценка

Что работает ПРАВИЛЬНО:

Загрузка настроек ESP Сохранение параметров Валидация диапазонов Откат при ошибках Обработка mock режима (Windows) Проверка прав доступа Визуальная индикация изменений Предупреждение о несохранённых изменениях WebSocket promise система Детальное логирование


🚀 Рекомендации для тестирования

1. Тест загрузки настроек

1. Открыть /config
2. Проверить, что отображаются текущие значения из esp.ini
3. Проверить логи: "[WS CFG-ESP-GET] ✅ Настройки ESP отправлены"

2. Тест сохранения (успех)

1. Изменить hz на 15000
2. Нажать "Применить"
3. Проверить, что кнопка исчезла
4. Проверить, что esp.ini обновлён
5. Проверить логи: "[WS CFG-ESP] ✅ Параметр hz успешно сохранён"

3. Тест валидации

1. Изменить hz на 999999 (вне диапазона)
2. Нажать "Применить"
3. Проверить ошибку: "Значение должно быть от 9999 до 100000"

4. Тест прав доступа

1. Войти как operator
2. Открыть /config
3. Проверить сообщение "Нет доступа"

5. Тест несохранённых изменений

1. Изменить hz (НЕ нажимать "Применить")
2. Нажать "Назад"
3. Проверить подтверждение "Есть несохранённые изменения..."

Анализ завершён: 2025-01-06 Все критические проблемы найдены и исправлены