"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OtaController = void 0; const firmware_1 = require("../models/firmware"); const ota_1 = require("../models/ota"); const device_1 = require("../models/device"); const fileUtils_1 = require("../utils/fileUtils"); const promises_1 = __importDefault(require("fs/promises")); const path_1 = __importDefault(require("path")); const mqttBrokerService_1 = require("../services/mqttBrokerService"); const loggerService_1 = require("../services/loggerService"); const helpers_1 = require("../utils/helpers"); const systemLog_1 = require("../models/systemLog"); const firmwareUploadDir = '/home/yangfei/OTA/firmware'; async function ensureUploadDirExists() { try { await promises_1.default.access(firmwareUploadDir); } catch { await promises_1.default.mkdir(firmwareUploadDir, { recursive: true }); } } ensureUploadDirExists(); class OtaController { static async logOTAToSystemLog(deviceId, message, level = 'info', details) { try { await systemLog_1.SystemLogModel.create({ level, message, source: 'ota', module: 'device_ota', details: details ? JSON.stringify({ ...details, deviceId }) : JSON.stringify({ deviceId }) }); } catch (error) { console.error('记录OTA系统日志失败:', error); } } static async uploadFirmware(req, res) { try { const file = req.file; if (!file) { res.status(400).json({ success: false, message: 'No file uploaded', error: 'No file uploaded' }); return; } if (!file.mimetype.startsWith('application/octet-stream')) { res.status(400).json({ success: false, message: 'Invalid file type. Only binary files are allowed.', error: 'Invalid file type. Only binary files are allowed.' }); return; } const version = req.body.version; if (!version || !/^\d+\.\d+\.\d+$/.test(version)) { res.status(400).json({ success: false, message: 'Invalid version format. Please use semantic versioning (e.g., 1.0.0).', error: 'Invalid version format. Please use semantic versioning (e.g., 1.0.0).' }); return; } const md5sum = await (0, fileUtils_1.generateMD5)(file.path); const newFilename = `firmware-${version}.bin`; const newFilePath = path_1.default.join(firmwareUploadDir, newFilename); await promises_1.default.rename(file.path, newFilePath); const firmwareData = { version, filename: newFilename, filepath: newFilePath, filesize: file.size, md5sum, description: req.body.description || '', status: req.body.status || 'active', created_by: req.user?.id || 'system' }; const firmware = await firmware_1.FirmwareFileModel.create(firmwareData); loggerService_1.LoggerService.info('固件上传成功', { source: 'ota', module: 'upload_firmware', details: JSON.stringify({ firmwareId: firmware.id, version, filename: newFilename, filesize: file.size, md5sum, createdBy: req.user?.id || 'system' }) }).catch(err => { console.error('固件上传成功日志写入失败:', err); }); res.status(201).json({ success: true, message: 'Firmware uploaded successfully', data: firmware }); } catch (error) { console.error('Error uploading firmware:', error); loggerService_1.LoggerService.error('固件上传失败', { source: 'ota', module: 'upload_firmware', details: JSON.stringify({ error: error instanceof Error ? error.message : '未知错误', version: req.body.version, filename: req.file?.originalname }) }).catch(err => { console.error('固件上传失败日志写入失败:', err); }); res.status(500).json({ success: false, message: 'Failed to upload firmware', error: error instanceof Error ? error.message : '未知错误' }); } } static async getFirmwareFiles(req, res) { try { const firmwareFiles = await firmware_1.FirmwareFileModel.getAll(); res.status(200).json({ success: true, message: '固件文件列表获取成功', data: firmwareFiles }); } catch (error) { console.error('Error getting firmware files:', error); res.status(500).json({ success: false, message: 'Failed to get firmware files', error: error instanceof Error ? error.message : '未知错误' }); } } static async deleteFirmware(req, res) { try { const id = parseInt((0, helpers_1.toString)(req.params.id)); const firmware = await firmware_1.FirmwareFileModel.getById(id); if (!firmware) { res.status(404).json({ success: false, message: 'Firmware not found', error: 'Firmware not found' }); return; } try { await promises_1.default.access(firmware.filepath); await promises_1.default.unlink(firmware.filepath); } catch (error) { console.error('Error deleting firmware file:', error); } await firmware_1.FirmwareFileModel.delete(id); res.status(200).json({ success: true, message: 'Firmware deleted successfully' }); } catch (error) { console.error('Error deleting firmware:', error); res.status(500).json({ success: false, message: 'Failed to delete firmware', error: error instanceof Error ? error.message : '未知错误' }); } } static async createOTATask(req, res) { try { const deviceId = (0, helpers_1.toString)(req.params.deviceId); const firmwareId = parseInt(req.body.firmwareId); const device = await device_1.DeviceModel.getByClientId(deviceId); if (!device) { res.status(404).json({ success: false, message: 'Device not found', error: 'Device not found' }); return; } const firmware = await firmware_1.FirmwareFileModel.getById(firmwareId); if (!firmware) { res.status(404).json({ success: false, message: 'Firmware not found', error: 'Firmware not found' }); return; } const taskData = { device_id: deviceId, firmware_id: firmwareId, status: 'pending', progress: 0, start_time: new Date() }; const task = await ota_1.OTATaskModel.create(taskData); await this.logOTAToSystemLog(deviceId, `OTA升级任务已创建,固件版本: ${firmware.version}`, 'info', { taskId: task.id, firmwareId: firmware.id, firmwareVersion: firmware.version, status: 'pending' }); if (device.status === 'online') { const mqttBroker = mqttBrokerService_1.MqttBrokerService.getInstance(); let otaServerUrl = process.env.OTA_SERVER_URL || process.env.BACKEND_URL || 'http://localhost:3002'; otaServerUrl = otaServerUrl.replace(/\/$/, ''); console.log('OTA Server URL:', otaServerUrl); console.log('Environment OTA_SERVER_URL:', process.env.OTA_SERVER_URL); console.log('Environment BACKEND_URL:', process.env.BACKEND_URL); const otaCommand = { act: 'upgrade', ver: firmware.version, url: `${otaServerUrl}/api/ota/firmware/${firmware.id}`, md5: firmware.md5sum, tid: task.id }; console.log('OTA升级指令:', JSON.stringify(otaCommand, null, 2)); console.log('MQTT Topic:', `device/${deviceId}/ota`); mqttBroker.publish(`device/${deviceId}/ota`, JSON.stringify(otaCommand)); loggerService_1.LoggerService.info('OTA升级任务创建成功', { source: 'ota', module: 'create_ota_task', details: JSON.stringify({ taskId: task.id, deviceId, deviceName: device.device_name, firmwareId: firmware.id, firmwareVersion: firmware.version, firmwareUrl: `${otaServerUrl}/api/ota/firmware/${firmware.id}`, deviceStatus: 'online', createdBy: req.user?.id || 'system' }) }).catch(err => { console.error('OTA任务创建成功日志写入失败:', err); }); } else { loggerService_1.LoggerService.info('OTA升级任务已创建(设备离线,等待上线后执行)', { source: 'ota', module: 'create_ota_task', details: JSON.stringify({ taskId: task.id, deviceId, deviceName: device.device_name, firmwareId: firmware.id, firmwareVersion: firmware.version, deviceStatus: 'offline' }) }).catch(err => { console.error('OTA离线任务创建日志写入失败:', err); }); } res.status(201).json({ success: true, message: device.status === 'online' ? 'OTA task created successfully' : 'OTA task created (device offline, will execute when online)', data: task }); } catch (error) { console.error('Error creating OTA task:', error); loggerService_1.LoggerService.error('OTA升级任务创建失败', { source: 'ota', module: 'create_ota_task', details: JSON.stringify({ deviceId: (0, helpers_1.toString)(req.params.deviceId), firmwareId: req.body.firmwareId, error: error instanceof Error ? error.message : '未知错误' }) }).catch(err => { console.error('OTA任务创建失败日志写入失败:', err); }); res.status(500).json({ success: false, message: 'Failed to create OTA task', error: error instanceof Error ? error.message : '未知错误' }); } } static async getDeviceOTATasks(req, res) { try { const deviceId = (0, helpers_1.toString)(req.params.deviceId); const tasks = await ota_1.OTATaskModel.getByDeviceId(deviceId); res.status(200).json({ success: true, message: 'Device OTA tasks retrieved successfully', data: tasks }); } catch (error) { console.error('Error getting device OTA tasks:', error); res.status(500).json({ success: false, message: 'Failed to get device OTA tasks', error: error instanceof Error ? error.message : '未知错误' }); } } static async getAllOTATasks(req, res) { try { const tasks = await ota_1.OTATaskModel.getAll(); res.status(200).json({ success: true, message: 'All OTA tasks retrieved successfully', data: tasks }); } catch (error) { console.error('Error getting all OTA tasks:', error); res.status(500).json({ success: false, message: 'Failed to get all OTA tasks', error: error instanceof Error ? error.message : '未知错误' }); } } static async downloadFirmware(req, res) { try { const id = parseInt((0, helpers_1.toString)(req.params.id)); const firmware = await firmware_1.FirmwareFileModel.getById(id); if (!firmware) { res.status(404).json({ success: false, message: 'Firmware not found', error: '固件文件不存在' }); return; } try { await promises_1.default.access(firmware.filepath); } catch { res.status(404).json({ success: false, message: 'Firmware file not found on server', error: '服务器上不存在固件文件' }); return; } res.download(firmware.filepath, firmware.filename); } catch (error) { console.error('Error downloading firmware:', error); res.status(500).json({ success: false, message: 'Failed to download firmware', error: error instanceof Error ? error.message : '未知错误' }); } } static async updateOTATaskStatus(req, res) { try { const taskId = parseInt((0, helpers_1.toString)(req.params.taskId)); const { status, progress, error_message } = req.body; const task = await ota_1.OTATaskModel.getById(taskId); if (!task) { res.status(404).json({ success: false, message: 'OTA task not found', error: 'OTA任务不存在' }); return; } if (status && progress !== undefined) { await ota_1.OTATaskModel.updateStatusAndProgress(taskId, status, progress); await this.logOTAToSystemLog(task.device_id, `OTA升级进度更新: ${progress}%, 状态: ${status}`, 'info', { taskId, status, progress }); } if (status === 'success' || status === 'failed') { await ota_1.OTATaskModel.updateResult(taskId, status, error_message); if (status === 'success') { const firmware = await firmware_1.FirmwareFileModel.getById(task.firmware_id); if (firmware) { await device_1.DeviceModel.update(task.device_id, { firmware_version: firmware.version }); } await this.logOTAToSystemLog(task.device_id, `OTA升级成功,固件版本已更新`, 'info', { taskId, firmwareId: task.firmware_id, status: 'success', progress: 100 }); loggerService_1.LoggerService.info('OTA升级任务完成', { source: 'ota', module: 'update_ota_task_status', details: JSON.stringify({ taskId, deviceId: task.device_id, firmwareId: task.firmware_id, status: 'success', progress: 100 }) }).catch(err => { console.error('OTA升级成功日志写入失败:', err); }); } else { await this.logOTAToSystemLog(task.device_id, `OTA升级失败: ${error_message || '未知错误'}`, 'error', { taskId, firmwareId: task.firmware_id, status: 'failed', errorMessage: error_message }); loggerService_1.LoggerService.error('OTA升级任务失败', { source: 'ota', module: 'update_ota_task_status', details: JSON.stringify({ taskId, deviceId: task.device_id, firmwareId: task.firmware_id, status: 'failed', errorMessage: error_message || '未知错误' }) }).catch(err => { console.error('OTA升级失败日志写入失败:', err); }); } } res.status(200).json({ success: true, message: 'OTA task status updated successfully', data: { taskId, status, progress } }); } catch (error) { console.error('Error updating OTA task status:', error); loggerService_1.LoggerService.error('OTA任务状态更新失败', { source: 'ota', module: 'update_ota_task_status', details: JSON.stringify({ taskId: req.params.taskId, status: req.body.status, progress: req.body.progress, error: error instanceof Error ? error.message : '未知错误' }) }).catch(err => { console.error('OTA任务状态更新失败日志写入失败:', err); }); res.status(500).json({ success: false, message: 'Failed to update OTA task status', error: error instanceof Error ? error.message : '未知错误' }); } } static async cancelOTATask(req, res) { try { const taskId = parseInt((0, helpers_1.toString)(req.params.taskId)); const task = await ota_1.OTATaskModel.getById(taskId); if (!task) { res.status(404).json({ success: false, message: 'OTA task not found', error: 'OTA任务不存在' }); return; } if (!['pending', 'downloading', 'installing'].includes(task.status)) { res.status(400).json({ success: false, message: 'Only pending, downloading, or installing tasks can be cancelled', error: '只有待处理、下载中或安装中的任务可以取消' }); return; } await ota_1.OTATaskModel.updateResult(taskId, 'failed', '用户取消'); await this.logOTAToSystemLog(task.device_id, `OTA升级任务已取消`, 'warn', { taskId, status: 'failed', reason: '用户取消' }); res.status(200).json({ success: true, message: 'OTA task cancelled successfully', data: { taskId, status: 'failed' } }); } catch (error) { console.error('Error cancelling OTA task:', error); res.status(500).json({ success: false, message: 'Failed to cancel OTA task', error: error instanceof Error ? error.message : '未知错误' }); } } static async retryOTATask(req, res) { try { const taskId = parseInt((0, helpers_1.toString)(req.params.taskId)); const task = await ota_1.OTATaskModel.getById(taskId); if (!task) { res.status(404).json({ success: false, message: 'OTA task not found', error: 'OTA任务不存在' }); return; } if (!['failed', 'pending'].includes(task.status)) { res.status(400).json({ success: false, message: 'Only failed or pending tasks can be retried', error: '只有失败或待处理的任务可以重试' }); return; } const device = await device_1.DeviceModel.getByClientId(task.device_id); if (!device) { res.status(404).json({ success: false, message: 'Device not found', error: '设备不存在' }); return; } const firmware = await firmware_1.FirmwareFileModel.getById(task.firmware_id); if (!firmware) { res.status(404).json({ success: false, message: 'Firmware not found', error: '固件不存在' }); return; } await ota_1.OTATaskModel.updateStatusAndProgress(taskId, 'pending', 0); const mqttBroker = mqttBrokerService_1.MqttBrokerService.getInstance(); let otaServerUrl = process.env.OTA_SERVER_URL || process.env.BACKEND_URL || 'http://localhost:3002'; otaServerUrl = otaServerUrl.replace(/\/$/, ''); const otaCommand = { act: 'upgrade', ver: firmware.version, url: `${otaServerUrl}/api/ota/firmware/${firmware.id}`, md5: firmware.md5sum, tid: taskId }; console.log('OTA升级指令(重试):', JSON.stringify(otaCommand, null, 2)); console.log('MQTT Topic:', `device/${task.device_id}/ota`); mqttBroker.publish(`device/${task.device_id}/ota`, JSON.stringify(otaCommand)); await this.logOTAToSystemLog(task.device_id, `OTA升级任务已重试,固件版本: ${firmware.version}`, 'info', { taskId, firmwareId: firmware.id, firmwareVersion: firmware.version, status: 'pending' }); res.status(200).json({ success: true, message: 'OTA task retried successfully', data: { taskId, status: 'pending' } }); } catch (error) { console.error('Error retrying OTA task:', error); res.status(500).json({ success: false, message: 'Failed to retry OTA task', error: error instanceof Error ? error.message : '未知错误' }); } } static async deleteOTATask(req, res) { try { const taskId = parseInt((0, helpers_1.toString)(req.params.taskId)); const task = await ota_1.OTATaskModel.getById(taskId); if (!task) { res.status(404).json({ success: false, message: 'OTA task not found', error: 'OTA任务不存在' }); return; } if (!['pending', 'failed'].includes(task.status)) { res.status(400).json({ success: false, message: 'Only pending or failed tasks can be deleted', error: '只有待处理或失败的任务可以删除' }); return; } await ota_1.OTATaskModel.delete(taskId); await this.logOTAToSystemLog(task.device_id, `OTA升级任务已删除`, 'info', { taskId, status: task.status, reason: '手动删除' }); res.status(200).json({ success: true, message: 'OTA task deleted successfully', data: { taskId } }); } catch (error) { console.error('Error deleting OTA task:', error); res.status(500).json({ success: false, message: 'Failed to delete OTA task', error: error instanceof Error ? error.message : '未知错误' }); } } static async bulkCreateOTATask(req, res) { try { const { firmwareId, deviceIds, retryCount = 3, retryInterval = 10000, timeout = 30000 } = req.body; if (!Array.isArray(deviceIds) || deviceIds.length === 0) { res.status(400).json({ success: false, message: 'Device IDs must be a non-empty array', error: '设备ID必须是非空数组' }); return; } const firmware = await firmware_1.FirmwareFileModel.getById(firmwareId); if (!firmware) { res.status(404).json({ success: false, message: 'Firmware not found', error: '固件不存在' }); return; } const validDevices = []; const invalidDevices = []; for (const deviceId of deviceIds) { const device = await device_1.DeviceModel.getByClientId(deviceId); if (device) { validDevices.push(deviceId); } else { invalidDevices.push(deviceId); } } if (validDevices.length === 0) { res.status(404).json({ success: false, message: 'No valid devices found', error: '未找到有效设备' }); return; } const createdTasks = []; const onlineTasks = []; const offlineTasks = []; const mqttBroker = mqttBrokerService_1.MqttBrokerService.getInstance(); const otaServerUrl = process.env.OTA_SERVER_URL || process.env.BACKEND_URL || 'http://localhost:3002'; for (const deviceId of validDevices) { const device = await device_1.DeviceModel.getByClientId(deviceId); const taskData = { device_id: deviceId, firmware_id: firmwareId, status: 'pending', progress: 0, start_time: new Date() }; const task = await ota_1.OTATaskModel.create(taskData); createdTasks.push(task); if (device && device.status === 'online') { onlineTasks.push({ taskId: task.id, deviceId }); const otaCommand = { act: 'upgrade', ver: firmware.version, url: `${otaServerUrl}/api/ota/firmware/${firmware.id}`, md5: firmware.md5sum, tid: task.id, rc: retryCount, ri: retryInterval, to: timeout }; console.log('OTA升级指令(批量):', JSON.stringify(otaCommand, null, 2)); console.log('MQTT Topic:', `device/${deviceId}/ota`); mqttBroker.publish(`device/${deviceId}/ota`, JSON.stringify(otaCommand)); } else { offlineTasks.push({ taskId: task.id, deviceId }); console.log(`设备 ${deviceId} 离线,OTA任务 ${task.id} 将在设备上线后自动执行`); } } loggerService_1.LoggerService.info('批量OTA任务创建完成', { source: 'ota', module: 'bulk_create_ota_task', details: JSON.stringify({ totalTasks: createdTasks.length, onlineTasks: onlineTasks.length, offlineTasks: offlineTasks.length, invalidDevices: invalidDevices.length }) }).catch(err => { console.error('批量OTA任务创建日志写入失败:', err); }); res.status(201).json({ success: true, message: `批量OTA任务创建成功(在线设备: ${onlineTasks.length}, 离线设备: ${offlineTasks.length})`, data: { tasks: createdTasks, validDevices: validDevices.length, invalidDevices: invalidDevices, onlineTasks: onlineTasks.length, offlineTasks: offlineTasks.length } }); } catch (error) { console.error('Error creating bulk OTA tasks:', error); res.status(500).json({ success: false, message: '批量OTA任务创建失败', error: error instanceof Error ? error.message : '未知错误' }); } } } exports.OtaController = OtaController; //# sourceMappingURL=otaController.js.map