23 KiB
🔍 Анализ логики настроек 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 Все критические проблемы найдены и исправлены ✅