/
h0tnanny
/
IotPlatform
Обзор
Документация
Войти
/
h0tnanny
/
IotPlatform
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
src/api/controllers/EmulatorController.ts
423 строки
12 KB
h0tnanny
Парсинг данных как строчки
20 фев 2026, 19:31
20 фев 2026, 19:31
fbb7684
Код
Авторство
О чём код?
import { Response } from 'express'; import { randomUUID } from 'crypto'; import { EmulatorService } from '../../services/EmulatorService'; import { EmulatorRepository, EmulatedDeviceRow, EmulatedDeviceVarRow, } from '../../repositories/EmulatorRepository'; import { Main } from '../../runtime/Main'; import { AuthRequest } from '../../middleware/auth'; export class EmulatorController { private readonly repository: EmulatorRepository; constructor( private readonly emulatorService: EmulatorService, private readonly main: Main ) { this.repository = new EmulatorRepository(); } /** * GET /emulator/devices — Список всех эмулируемых устройств */ getAll = async (_req: AuthRequest, res: Response): Promise<void> => { try { const devices = this.emulatorService.getAllDevices(); res.json({ data: devices.map((d) => this.formatDevice(d.device, d.variables, d.currentValues)), }); } catch (error) { console.error('[EmulatorController] Ошибка получения устройств:', error); res.status(500).json({ error: { code: 'INTERNAL_ERROR', message: 'Ошибка получения устройств' }, }); } }; /** * GET /emulator/devices/:id — Получить устройство по ID */ getById = async (req: AuthRequest, res: Response): Promise<void> => { const { id } = req.params; const running = this.emulatorService.getDevice(id); if (!running) { res.status(404).json({ error: { code: 'NOT_FOUND', message: `Устройство с ID "${id}" не найдено` }, }); return; } res.json({ data: this.formatDevice(running.device, running.variables, running.currentValues), }); }; /** * POST /emulator/devices — Создать эмулируемое устройство */ create = async (req: AuthRequest, res: Response): Promise<void> => { const { name, description, mode, targetEquipmentId, pushInterval, udpPort, variables, payloadSimulationFormat, } = req.body; // Валидация if (!name) { res.status(400).json({ error: { code: 'VALIDATION_ERROR', message: 'Поле name обязательно' }, }); return; } if (mode !== 'server' && mode !== 'client') { res.status(400).json({ error: { code: 'VALIDATION_ERROR', message: 'mode должен быть "server" или "client"', }, }); return; } if (mode === 'client') { if (!targetEquipmentId) { res.status(400).json({ error: { code: 'VALIDATION_ERROR', message: 'Для client-режима необходим targetEquipmentId', }, }); return; } // Проверяем что equipment существует const equipment = this.main.getEquipment(targetEquipmentId); if (!equipment) { res.status(400).json({ error: { code: 'VALIDATION_ERROR', message: `Equipment с ID "${targetEquipmentId}" не найден`, }, }); return; } } if (!variables || !Array.isArray(variables) || variables.length === 0) { res.status(400).json({ error: { code: 'VALIDATION_ERROR', message: 'variables должен быть непустым массивом', }, }); return; } const deviceId = randomUUID(); const serverPath = mode === 'server' ? `/emulator/devices/${deviceId}/data` : null; const deviceRow: EmulatedDeviceRow = { id: deviceId, name, description: description || null, mode, is_running: false, server_path: serverPath, udp_port: udpPort || null, target_equipment_id: targetEquipmentId || null, push_interval: pushInterval || 5000, payload_simulation_format: payloadSimulationFormat === 'key_value' ? 'key_value' : payloadSimulationFormat === 'positional' ? 'positional' : undefined, }; const varRows: EmulatedDeviceVarRow[] = variables.map( (v: { name: string; description?: string; type: string; genConfig?: Record<string, unknown> }) => ({ device_id: deviceId, name: v.name, description: v.description || null, type: v.type, gen_config: v.genConfig || {}, }) ); try { await this.repository.save(deviceRow, varRows); this.emulatorService.addDevice(deviceRow, varRows); res.status(201).json({ data: this.formatDevice(deviceRow, varRows, {}), }); } catch (error) { console.error('[EmulatorController] Ошибка создания устройства:', error); res.status(500).json({ error: { code: 'INTERNAL_ERROR', message: 'Ошибка создания устройства' }, }); } }; /** * PUT /emulator/devices/:id — Обновить устройство */ update = async (req: AuthRequest, res: Response): Promise<void> => { const { id } = req.params; const { name, description, mode, targetEquipmentId, pushInterval, udpPort, variables, payloadSimulationFormat, } = req.body; const existing = this.emulatorService.getDevice(id); if (!existing) { res.status(404).json({ error: { code: 'NOT_FOUND', message: `Устройство с ID "${id}" не найдено` }, }); return; } // Валидация (аналогично create) if (!name) { res.status(400).json({ error: { code: 'VALIDATION_ERROR', message: 'Поле name обязательно' }, }); return; } if (mode !== 'server' && mode !== 'client') { res.status(400).json({ error: { code: 'VALIDATION_ERROR', message: 'mode должен быть "server" или "client"', }, }); return; } if (mode === 'client' && targetEquipmentId) { const equipment = this.main.getEquipment(targetEquipmentId); if (!equipment) { res.status(400).json({ error: { code: 'VALIDATION_ERROR', message: `Equipment с ID "${targetEquipmentId}" не найден`, }, }); return; } } if (!variables || !Array.isArray(variables) || variables.length === 0) { res.status(400).json({ error: { code: 'VALIDATION_ERROR', message: 'variables должен быть непустым массивом', }, }); return; } const wasRunning = existing.device.is_running; // Останавливаем устройство если было запущено if (wasRunning) { await this.emulatorService.stopDevice(id); } const serverPath = mode === 'server' ? `/emulator/devices/${id}/data` : null; const deviceRow: EmulatedDeviceRow = { id, name, description: description || null, mode, is_running: false, server_path: serverPath, udp_port: udpPort || null, target_equipment_id: targetEquipmentId || null, push_interval: pushInterval || 5000, payload_simulation_format: payloadSimulationFormat === 'key_value' ? 'key_value' : payloadSimulationFormat === 'positional' ? 'positional' : undefined, }; const varRows: EmulatedDeviceVarRow[] = variables.map( (v: { name: string; description?: string; type: string; genConfig?: Record<string, unknown> }) => ({ device_id: id, name: v.name, description: v.description || null, type: v.type, gen_config: v.genConfig || {}, }) ); try { await this.repository.save(deviceRow, varRows); this.emulatorService.updateDevice(deviceRow, varRows); // Если было запущено — перезапускаем if (wasRunning) { await this.emulatorService.startDevice(id); } const running = this.emulatorService.getDevice(id); res.json({ data: this.formatDevice( running?.device ?? deviceRow, running?.variables ?? varRows, running?.currentValues ?? {} ), }); } catch (error) { console.error('[EmulatorController] Ошибка обновления устройства:', error); res.status(500).json({ error: { code: 'INTERNAL_ERROR', message: 'Ошибка обновления устройства' }, }); } }; /** * DELETE /emulator/devices/:id — Удалить устройство */ delete = async (req: AuthRequest, res: Response): Promise<void> => { const { id } = req.params; const existing = this.emulatorService.getDevice(id); if (!existing) { res.status(404).json({ error: { code: 'NOT_FOUND', message: `Устройство с ID "${id}" не найдено` }, }); return; } try { this.emulatorService.removeDevice(id); await this.repository.delete(id); res.json({ data: { id, message: `Устройство "${existing.device.name}" удалено` }, }); } catch (error) { console.error('[EmulatorController] Ошибка удаления устройства:', error); res.status(500).json({ error: { code: 'INTERNAL_ERROR', message: 'Ошибка удаления устройства' }, }); } }; /** * POST /emulator/devices/:id/start — Запустить эмуляцию */ start = async (req: AuthRequest, res: Response): Promise<void> => { const { id } = req.params; try { await this.emulatorService.startDevice(id); const running = this.emulatorService.getDevice(id); res.json({ data: { id, isRunning: true, message: `Устройство "${running?.device.name}" запущено`, }, }); } catch (error) { const message = error instanceof Error ? error.message : 'Ошибка запуска'; res.status(400).json({ error: { code: 'START_ERROR', message }, }); } }; /** * POST /emulator/devices/:id/stop — Остановить эмуляцию */ stop = async (req: AuthRequest, res: Response): Promise<void> => { const { id } = req.params; try { await this.emulatorService.stopDevice(id); const running = this.emulatorService.getDevice(id); res.json({ data: { id, isRunning: false, message: `Устройство "${running?.device.name}" остановлено`, }, }); } catch (error) { const message = error instanceof Error ? error.message : 'Ошибка остановки'; res.status(400).json({ error: { code: 'STOP_ERROR', message }, }); } }; /** * GET /emulator/devices/:id/values — Текущие значения устройства */ getValues = async (req: AuthRequest, res: Response): Promise<void> => { const { id } = req.params; const values = this.emulatorService.getDeviceValues(id); if (values === null) { res.status(404).json({ error: { code: 'NOT_FOUND', message: `Устройство с ID "${id}" не найдено` }, }); return; } res.json({ data: values }); }; /** * Форматирует устройство для API ответа */ private formatDevice( device: EmulatedDeviceRow, variables: EmulatedDeviceVarRow[], currentValues: Record<string, unknown> ) { return { id: device.id, name: device.name, description: device.description, mode: device.mode, isRunning: device.is_running, serverPath: device.server_path, udpPort: device.udp_port, targetEquipmentId: device.target_equipment_id, pushInterval: device.push_interval, payloadSimulationFormat: device.payload_simulation_format ?? 'json', variables: variables.map((v) => ({ name: v.name, description: v.description, type: v.type, genConfig: v.gen_config, })), currentValues, }; } }