// Эмуляция SerialPort для разработки интерфейса class MockSerialPort { constructor(options) { this.path = options.path; this.baudRate = options.baudRate; this.isOpen = false; this.listeners = {}; this.lastPingTime = 0; // Добавляем отслеживание времени последнего ping console.log(`[MOCK SERIAL] Создан порт ${this.path} со скоростью ${this.baudRate}`); // Эмулируем открытие порта через небольшую задержку setTimeout(() => { this.isOpen = true; this.emit('open'); console.log(`[MOCK SERIAL] Порт ${this.path} открыт`); }, 100); } on(event, callback) { if (!this.listeners[event]) { this.listeners[event] = []; } this.listeners[event].push(callback); } write(data) { console.log(`[MOCK SERIAL] Отправка: ${data.trim()}`); // Проверяем частоту ping команд if (data.includes('ping')) { const now = Date.now(); const timeSinceLastPing = now - this.lastPingTime; // Если ping отправляется слишком часто (менее 2 секунд), пропускаем if (timeSinceLastPing < 2000) { console.log(`[MOCK SERIAL] ⏱️ Ping отправлен слишком часто (${timeSinceLastPing}ms), пропускаем ответ`); return true; } this.lastPingTime = now; } // Эмулируем ответы ESP через небольшую задержку setTimeout(() => { if (data.includes('ping')) { // Отвечаем на ping this.emit('data', 'pong\n'); console.log(`[MOCK SERIAL] ✅ Отвечаем на ping: pong`); } else if (data.includes('start=')) { // Эмулируем запуск игры const match = data.match(/start=(\d+)\s+(\d+)/); if (match) { const line = match[1]; const patr = match[2]; // Отправляем полный ответ как ожидает сервер this.emit('data', `start,${line},${patr}\n`); console.log(`[MOCK SERIAL] ✅ Эмулируем УСПЕШНЫЙ запуск игры на линии ${line} с ${patr} патронами`); } } else if (data.includes('end=')) { // Эмулируем завершение игры const match = data.match(/end=(\d+)/); if (match) { const line = match[1]; this.emit('data', `end,${line}\n`); console.log(`[MOCK SERIAL] ✅ Эмулируем завершение игры на линии ${line}`); } } else if (data.includes('move=')) { // Эмулируем перемещение игры const match = data.match(/move=(\d+)\s+(\d+)/); if (match) { const fromLine = match[1]; const toLine = match[2]; this.emit('data', `move,${fromLine},${toLine}\n`); console.log(`[MOCK SERIAL] ✅ Эмулируем перемещение игры с линии ${fromLine} на ${toLine}`); } } else if (data.includes('pause;')) { // Эмулируем установку паузы this.emit('data', 'pause,\n'); console.log(`[MOCK SERIAL] ✅ Эмулируем установку системы на паузу`); } else if (data.includes('resume;')) { // Эмулируем снятие паузы this.emit('data', 'resume,\n'); console.log(`[MOCK SERIAL] ✅ Эмулируем снятие системы с паузы`); } }, 200); return true; } pipe(parser) { this.parser = parser; return parser; } emit(event, data) { // Уведомляем слушателей порта if (this.listeners[event]) { this.listeners[event].forEach(callback => { try { callback(data); } catch (error) { console.error(`[MOCK SERIAL] Ошибка в обработчике ${event}:`, error); } }); } // Также уведомляем parser, если это событие data if (this.parser && event === 'data') { this.parser.emit('data', data); } } close(callback) { this.isOpen = false; if (callback) callback(); console.log(`[MOCK SERIAL] Порт ${this.path} закрыт`); } } class MockReadlineParser { constructor(options) { this.delimiter = options.delimiter || '\n'; this.listeners = {}; console.log(`[MOCK PARSER] Создан парсер с разделителем: ${JSON.stringify(this.delimiter)}`); } on(event, callback) { if (!this.listeners[event]) { this.listeners[event] = []; } this.listeners[event].push(callback); } emit(event, data) { if (this.listeners[event]) { this.listeners[event].forEach(callback => { try { callback(data); } catch (error) { console.error(`[MOCK PARSER] Ошибка в обработчике ${event}:`, error); } }); } } } module.exports = { SerialPort: MockSerialPort, ReadlineParser: MockReadlineParser };