otaController.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. exports.OtaController = void 0;
  7. const firmware_1 = require("../models/firmware");
  8. const ota_1 = require("../models/ota");
  9. const device_1 = require("../models/device");
  10. const fileUtils_1 = require("../utils/fileUtils");
  11. const promises_1 = __importDefault(require("fs/promises"));
  12. const path_1 = __importDefault(require("path"));
  13. const mqttBrokerService_1 = require("../services/mqttBrokerService");
  14. const loggerService_1 = require("../services/loggerService");
  15. const helpers_1 = require("../utils/helpers");
  16. const systemLog_1 = require("../models/systemLog");
  17. const firmwareUploadDir = '/home/yangfei/OTA/firmware';
  18. async function ensureUploadDirExists() {
  19. try {
  20. await promises_1.default.access(firmwareUploadDir);
  21. }
  22. catch {
  23. await promises_1.default.mkdir(firmwareUploadDir, { recursive: true });
  24. }
  25. }
  26. ensureUploadDirExists();
  27. class OtaController {
  28. static async logOTAToSystemLog(deviceId, message, level = 'info', details) {
  29. try {
  30. await systemLog_1.SystemLogModel.create({
  31. level,
  32. message,
  33. source: 'ota',
  34. module: 'device_ota',
  35. details: details ? JSON.stringify({ ...details, deviceId }) : JSON.stringify({ deviceId })
  36. });
  37. }
  38. catch (error) {
  39. console.error('记录OTA系统日志失败:', error);
  40. }
  41. }
  42. static async uploadFirmware(req, res) {
  43. try {
  44. const file = req.file;
  45. if (!file) {
  46. res.status(400).json({
  47. success: false,
  48. message: 'No file uploaded',
  49. error: 'No file uploaded'
  50. });
  51. return;
  52. }
  53. if (!file.mimetype.startsWith('application/octet-stream')) {
  54. res.status(400).json({
  55. success: false,
  56. message: 'Invalid file type. Only binary files are allowed.',
  57. error: 'Invalid file type. Only binary files are allowed.'
  58. });
  59. return;
  60. }
  61. const version = req.body.version;
  62. if (!version || !/^\d+\.\d+\.\d+$/.test(version)) {
  63. res.status(400).json({
  64. success: false,
  65. message: 'Invalid version format. Please use semantic versioning (e.g., 1.0.0).',
  66. error: 'Invalid version format. Please use semantic versioning (e.g., 1.0.0).'
  67. });
  68. return;
  69. }
  70. const md5sum = await (0, fileUtils_1.generateMD5)(file.path);
  71. const newFilename = `firmware-${version}.bin`;
  72. const newFilePath = path_1.default.join(firmwareUploadDir, newFilename);
  73. await promises_1.default.rename(file.path, newFilePath);
  74. const firmwareData = {
  75. version,
  76. filename: newFilename,
  77. filepath: newFilePath,
  78. filesize: file.size,
  79. md5sum,
  80. description: req.body.description || '',
  81. status: req.body.status || 'active',
  82. created_by: req.user?.id || 'system'
  83. };
  84. const firmware = await firmware_1.FirmwareFileModel.create(firmwareData);
  85. loggerService_1.LoggerService.info('固件上传成功', {
  86. source: 'ota',
  87. module: 'upload_firmware',
  88. details: JSON.stringify({
  89. firmwareId: firmware.id,
  90. version,
  91. filename: newFilename,
  92. filesize: file.size,
  93. md5sum,
  94. createdBy: req.user?.id || 'system'
  95. })
  96. }).catch(err => {
  97. console.error('固件上传成功日志写入失败:', err);
  98. });
  99. res.status(201).json({
  100. success: true,
  101. message: 'Firmware uploaded successfully',
  102. data: firmware
  103. });
  104. }
  105. catch (error) {
  106. console.error('Error uploading firmware:', error);
  107. loggerService_1.LoggerService.error('固件上传失败', {
  108. source: 'ota',
  109. module: 'upload_firmware',
  110. details: JSON.stringify({
  111. error: error instanceof Error ? error.message : '未知错误',
  112. version: req.body.version,
  113. filename: req.file?.originalname
  114. })
  115. }).catch(err => {
  116. console.error('固件上传失败日志写入失败:', err);
  117. });
  118. res.status(500).json({
  119. success: false,
  120. message: 'Failed to upload firmware',
  121. error: error instanceof Error ? error.message : '未知错误'
  122. });
  123. }
  124. }
  125. static async getFirmwareFiles(req, res) {
  126. try {
  127. const firmwareFiles = await firmware_1.FirmwareFileModel.getAll();
  128. res.status(200).json({
  129. success: true,
  130. message: '固件文件列表获取成功',
  131. data: firmwareFiles
  132. });
  133. }
  134. catch (error) {
  135. console.error('Error getting firmware files:', error);
  136. res.status(500).json({
  137. success: false,
  138. message: 'Failed to get firmware files',
  139. error: error instanceof Error ? error.message : '未知错误'
  140. });
  141. }
  142. }
  143. static async deleteFirmware(req, res) {
  144. try {
  145. const id = parseInt((0, helpers_1.toString)(req.params.id));
  146. const firmware = await firmware_1.FirmwareFileModel.getById(id);
  147. if (!firmware) {
  148. res.status(404).json({
  149. success: false,
  150. message: 'Firmware not found',
  151. error: 'Firmware not found'
  152. });
  153. return;
  154. }
  155. try {
  156. await promises_1.default.access(firmware.filepath);
  157. await promises_1.default.unlink(firmware.filepath);
  158. }
  159. catch (error) {
  160. console.error('Error deleting firmware file:', error);
  161. }
  162. await firmware_1.FirmwareFileModel.delete(id);
  163. res.status(200).json({
  164. success: true,
  165. message: 'Firmware deleted successfully'
  166. });
  167. }
  168. catch (error) {
  169. console.error('Error deleting firmware:', error);
  170. res.status(500).json({
  171. success: false,
  172. message: 'Failed to delete firmware',
  173. error: error instanceof Error ? error.message : '未知错误'
  174. });
  175. }
  176. }
  177. static async createOTATask(req, res) {
  178. try {
  179. const deviceId = (0, helpers_1.toString)(req.params.deviceId);
  180. const firmwareId = parseInt(req.body.firmwareId);
  181. const device = await device_1.DeviceModel.getByClientId(deviceId);
  182. if (!device) {
  183. res.status(404).json({
  184. success: false,
  185. message: 'Device not found',
  186. error: 'Device not found'
  187. });
  188. return;
  189. }
  190. const firmware = await firmware_1.FirmwareFileModel.getById(firmwareId);
  191. if (!firmware) {
  192. res.status(404).json({
  193. success: false,
  194. message: 'Firmware not found',
  195. error: 'Firmware not found'
  196. });
  197. return;
  198. }
  199. const taskData = {
  200. device_id: deviceId,
  201. firmware_id: firmwareId,
  202. status: 'pending',
  203. progress: 0,
  204. start_time: new Date()
  205. };
  206. const task = await ota_1.OTATaskModel.create(taskData);
  207. await this.logOTAToSystemLog(deviceId, `OTA升级任务已创建,固件版本: ${firmware.version}`, 'info', {
  208. taskId: task.id,
  209. firmwareId: firmware.id,
  210. firmwareVersion: firmware.version,
  211. status: 'pending'
  212. });
  213. if (device.status === 'online') {
  214. const mqttBroker = mqttBrokerService_1.MqttBrokerService.getInstance();
  215. let otaServerUrl = process.env.OTA_SERVER_URL || process.env.BACKEND_URL || 'http://localhost:3002';
  216. otaServerUrl = otaServerUrl.replace(/\/$/, '');
  217. console.log('OTA Server URL:', otaServerUrl);
  218. console.log('Environment OTA_SERVER_URL:', process.env.OTA_SERVER_URL);
  219. console.log('Environment BACKEND_URL:', process.env.BACKEND_URL);
  220. const otaCommand = {
  221. act: 'upgrade',
  222. ver: firmware.version,
  223. url: `${otaServerUrl}/api/ota/firmware/${firmware.id}`,
  224. md5: firmware.md5sum,
  225. tid: task.id
  226. };
  227. console.log('OTA升级指令:', JSON.stringify(otaCommand, null, 2));
  228. console.log('MQTT Topic:', `device/${deviceId}/ota`);
  229. mqttBroker.publish(`device/${deviceId}/ota`, JSON.stringify(otaCommand));
  230. loggerService_1.LoggerService.info('OTA升级任务创建成功', {
  231. source: 'ota',
  232. module: 'create_ota_task',
  233. details: JSON.stringify({
  234. taskId: task.id,
  235. deviceId,
  236. deviceName: device.device_name,
  237. firmwareId: firmware.id,
  238. firmwareVersion: firmware.version,
  239. firmwareUrl: `${otaServerUrl}/api/ota/firmware/${firmware.id}`,
  240. deviceStatus: 'online',
  241. createdBy: req.user?.id || 'system'
  242. })
  243. }).catch(err => {
  244. console.error('OTA任务创建成功日志写入失败:', err);
  245. });
  246. }
  247. else {
  248. loggerService_1.LoggerService.info('OTA升级任务已创建(设备离线,等待上线后执行)', {
  249. source: 'ota',
  250. module: 'create_ota_task',
  251. details: JSON.stringify({
  252. taskId: task.id,
  253. deviceId,
  254. deviceName: device.device_name,
  255. firmwareId: firmware.id,
  256. firmwareVersion: firmware.version,
  257. deviceStatus: 'offline'
  258. })
  259. }).catch(err => {
  260. console.error('OTA离线任务创建日志写入失败:', err);
  261. });
  262. }
  263. res.status(201).json({
  264. success: true,
  265. message: device.status === 'online' ? 'OTA task created successfully' : 'OTA task created (device offline, will execute when online)',
  266. data: task
  267. });
  268. }
  269. catch (error) {
  270. console.error('Error creating OTA task:', error);
  271. loggerService_1.LoggerService.error('OTA升级任务创建失败', {
  272. source: 'ota',
  273. module: 'create_ota_task',
  274. details: JSON.stringify({
  275. deviceId: (0, helpers_1.toString)(req.params.deviceId),
  276. firmwareId: req.body.firmwareId,
  277. error: error instanceof Error ? error.message : '未知错误'
  278. })
  279. }).catch(err => {
  280. console.error('OTA任务创建失败日志写入失败:', err);
  281. });
  282. res.status(500).json({
  283. success: false,
  284. message: 'Failed to create OTA task',
  285. error: error instanceof Error ? error.message : '未知错误'
  286. });
  287. }
  288. }
  289. static async getDeviceOTATasks(req, res) {
  290. try {
  291. const deviceId = (0, helpers_1.toString)(req.params.deviceId);
  292. const tasks = await ota_1.OTATaskModel.getByDeviceId(deviceId);
  293. res.status(200).json({
  294. success: true,
  295. message: 'Device OTA tasks retrieved successfully',
  296. data: tasks
  297. });
  298. }
  299. catch (error) {
  300. console.error('Error getting device OTA tasks:', error);
  301. res.status(500).json({
  302. success: false,
  303. message: 'Failed to get device OTA tasks',
  304. error: error instanceof Error ? error.message : '未知错误'
  305. });
  306. }
  307. }
  308. static async getAllOTATasks(req, res) {
  309. try {
  310. const tasks = await ota_1.OTATaskModel.getAll();
  311. res.status(200).json({
  312. success: true,
  313. message: 'All OTA tasks retrieved successfully',
  314. data: tasks
  315. });
  316. }
  317. catch (error) {
  318. console.error('Error getting all OTA tasks:', error);
  319. res.status(500).json({
  320. success: false,
  321. message: 'Failed to get all OTA tasks',
  322. error: error instanceof Error ? error.message : '未知错误'
  323. });
  324. }
  325. }
  326. static async downloadFirmware(req, res) {
  327. try {
  328. const id = parseInt((0, helpers_1.toString)(req.params.id));
  329. const firmware = await firmware_1.FirmwareFileModel.getById(id);
  330. if (!firmware) {
  331. res.status(404).json({
  332. success: false,
  333. message: 'Firmware not found',
  334. error: '固件文件不存在'
  335. });
  336. return;
  337. }
  338. try {
  339. await promises_1.default.access(firmware.filepath);
  340. }
  341. catch {
  342. res.status(404).json({
  343. success: false,
  344. message: 'Firmware file not found on server',
  345. error: '服务器上不存在固件文件'
  346. });
  347. return;
  348. }
  349. res.download(firmware.filepath, firmware.filename);
  350. }
  351. catch (error) {
  352. console.error('Error downloading firmware:', error);
  353. res.status(500).json({
  354. success: false,
  355. message: 'Failed to download firmware',
  356. error: error instanceof Error ? error.message : '未知错误'
  357. });
  358. }
  359. }
  360. static async updateOTATaskStatus(req, res) {
  361. try {
  362. const taskId = parseInt((0, helpers_1.toString)(req.params.taskId));
  363. const { status, progress, error_message } = req.body;
  364. const task = await ota_1.OTATaskModel.getById(taskId);
  365. if (!task) {
  366. res.status(404).json({
  367. success: false,
  368. message: 'OTA task not found',
  369. error: 'OTA任务不存在'
  370. });
  371. return;
  372. }
  373. if (status && progress !== undefined) {
  374. await ota_1.OTATaskModel.updateStatusAndProgress(taskId, status, progress);
  375. await this.logOTAToSystemLog(task.device_id, `OTA升级进度更新: ${progress}%, 状态: ${status}`, 'info', {
  376. taskId,
  377. status,
  378. progress
  379. });
  380. }
  381. if (status === 'success' || status === 'failed') {
  382. await ota_1.OTATaskModel.updateResult(taskId, status, error_message);
  383. if (status === 'success') {
  384. const firmware = await firmware_1.FirmwareFileModel.getById(task.firmware_id);
  385. if (firmware) {
  386. await device_1.DeviceModel.update(task.device_id, { firmware_version: firmware.version });
  387. }
  388. await this.logOTAToSystemLog(task.device_id, `OTA升级成功,固件版本已更新`, 'info', {
  389. taskId,
  390. firmwareId: task.firmware_id,
  391. status: 'success',
  392. progress: 100
  393. });
  394. loggerService_1.LoggerService.info('OTA升级任务完成', {
  395. source: 'ota',
  396. module: 'update_ota_task_status',
  397. details: JSON.stringify({
  398. taskId,
  399. deviceId: task.device_id,
  400. firmwareId: task.firmware_id,
  401. status: 'success',
  402. progress: 100
  403. })
  404. }).catch(err => {
  405. console.error('OTA升级成功日志写入失败:', err);
  406. });
  407. }
  408. else {
  409. await this.logOTAToSystemLog(task.device_id, `OTA升级失败: ${error_message || '未知错误'}`, 'error', {
  410. taskId,
  411. firmwareId: task.firmware_id,
  412. status: 'failed',
  413. errorMessage: error_message
  414. });
  415. loggerService_1.LoggerService.error('OTA升级任务失败', {
  416. source: 'ota',
  417. module: 'update_ota_task_status',
  418. details: JSON.stringify({
  419. taskId,
  420. deviceId: task.device_id,
  421. firmwareId: task.firmware_id,
  422. status: 'failed',
  423. errorMessage: error_message || '未知错误'
  424. })
  425. }).catch(err => {
  426. console.error('OTA升级失败日志写入失败:', err);
  427. });
  428. }
  429. }
  430. res.status(200).json({
  431. success: true,
  432. message: 'OTA task status updated successfully',
  433. data: { taskId, status, progress }
  434. });
  435. }
  436. catch (error) {
  437. console.error('Error updating OTA task status:', error);
  438. loggerService_1.LoggerService.error('OTA任务状态更新失败', {
  439. source: 'ota',
  440. module: 'update_ota_task_status',
  441. details: JSON.stringify({
  442. taskId: req.params.taskId,
  443. status: req.body.status,
  444. progress: req.body.progress,
  445. error: error instanceof Error ? error.message : '未知错误'
  446. })
  447. }).catch(err => {
  448. console.error('OTA任务状态更新失败日志写入失败:', err);
  449. });
  450. res.status(500).json({
  451. success: false,
  452. message: 'Failed to update OTA task status',
  453. error: error instanceof Error ? error.message : '未知错误'
  454. });
  455. }
  456. }
  457. static async cancelOTATask(req, res) {
  458. try {
  459. const taskId = parseInt((0, helpers_1.toString)(req.params.taskId));
  460. const task = await ota_1.OTATaskModel.getById(taskId);
  461. if (!task) {
  462. res.status(404).json({
  463. success: false,
  464. message: 'OTA task not found',
  465. error: 'OTA任务不存在'
  466. });
  467. return;
  468. }
  469. if (!['pending', 'downloading', 'installing'].includes(task.status)) {
  470. res.status(400).json({
  471. success: false,
  472. message: 'Only pending, downloading, or installing tasks can be cancelled',
  473. error: '只有待处理、下载中或安装中的任务可以取消'
  474. });
  475. return;
  476. }
  477. await ota_1.OTATaskModel.updateResult(taskId, 'failed', '用户取消');
  478. await this.logOTAToSystemLog(task.device_id, `OTA升级任务已取消`, 'warn', {
  479. taskId,
  480. status: 'failed',
  481. reason: '用户取消'
  482. });
  483. res.status(200).json({
  484. success: true,
  485. message: 'OTA task cancelled successfully',
  486. data: { taskId, status: 'failed' }
  487. });
  488. }
  489. catch (error) {
  490. console.error('Error cancelling OTA task:', error);
  491. res.status(500).json({
  492. success: false,
  493. message: 'Failed to cancel OTA task',
  494. error: error instanceof Error ? error.message : '未知错误'
  495. });
  496. }
  497. }
  498. static async retryOTATask(req, res) {
  499. try {
  500. const taskId = parseInt((0, helpers_1.toString)(req.params.taskId));
  501. const task = await ota_1.OTATaskModel.getById(taskId);
  502. if (!task) {
  503. res.status(404).json({
  504. success: false,
  505. message: 'OTA task not found',
  506. error: 'OTA任务不存在'
  507. });
  508. return;
  509. }
  510. if (!['failed', 'pending'].includes(task.status)) {
  511. res.status(400).json({
  512. success: false,
  513. message: 'Only failed or pending tasks can be retried',
  514. error: '只有失败或待处理的任务可以重试'
  515. });
  516. return;
  517. }
  518. const device = await device_1.DeviceModel.getByClientId(task.device_id);
  519. if (!device) {
  520. res.status(404).json({
  521. success: false,
  522. message: 'Device not found',
  523. error: '设备不存在'
  524. });
  525. return;
  526. }
  527. const firmware = await firmware_1.FirmwareFileModel.getById(task.firmware_id);
  528. if (!firmware) {
  529. res.status(404).json({
  530. success: false,
  531. message: 'Firmware not found',
  532. error: '固件不存在'
  533. });
  534. return;
  535. }
  536. await ota_1.OTATaskModel.updateStatusAndProgress(taskId, 'pending', 0);
  537. const mqttBroker = mqttBrokerService_1.MqttBrokerService.getInstance();
  538. let otaServerUrl = process.env.OTA_SERVER_URL || process.env.BACKEND_URL || 'http://localhost:3002';
  539. otaServerUrl = otaServerUrl.replace(/\/$/, '');
  540. const otaCommand = {
  541. act: 'upgrade',
  542. ver: firmware.version,
  543. url: `${otaServerUrl}/api/ota/firmware/${firmware.id}`,
  544. md5: firmware.md5sum,
  545. tid: taskId
  546. };
  547. console.log('OTA升级指令(重试):', JSON.stringify(otaCommand, null, 2));
  548. console.log('MQTT Topic:', `device/${task.device_id}/ota`);
  549. mqttBroker.publish(`device/${task.device_id}/ota`, JSON.stringify(otaCommand));
  550. await this.logOTAToSystemLog(task.device_id, `OTA升级任务已重试,固件版本: ${firmware.version}`, 'info', {
  551. taskId,
  552. firmwareId: firmware.id,
  553. firmwareVersion: firmware.version,
  554. status: 'pending'
  555. });
  556. res.status(200).json({
  557. success: true,
  558. message: 'OTA task retried successfully',
  559. data: { taskId, status: 'pending' }
  560. });
  561. }
  562. catch (error) {
  563. console.error('Error retrying OTA task:', error);
  564. res.status(500).json({
  565. success: false,
  566. message: 'Failed to retry OTA task',
  567. error: error instanceof Error ? error.message : '未知错误'
  568. });
  569. }
  570. }
  571. static async deleteOTATask(req, res) {
  572. try {
  573. const taskId = parseInt((0, helpers_1.toString)(req.params.taskId));
  574. const task = await ota_1.OTATaskModel.getById(taskId);
  575. if (!task) {
  576. res.status(404).json({
  577. success: false,
  578. message: 'OTA task not found',
  579. error: 'OTA任务不存在'
  580. });
  581. return;
  582. }
  583. if (!['pending', 'failed'].includes(task.status)) {
  584. res.status(400).json({
  585. success: false,
  586. message: 'Only pending or failed tasks can be deleted',
  587. error: '只有待处理或失败的任务可以删除'
  588. });
  589. return;
  590. }
  591. await ota_1.OTATaskModel.delete(taskId);
  592. await this.logOTAToSystemLog(task.device_id, `OTA升级任务已删除`, 'info', {
  593. taskId,
  594. status: task.status,
  595. reason: '手动删除'
  596. });
  597. res.status(200).json({
  598. success: true,
  599. message: 'OTA task deleted successfully',
  600. data: { taskId }
  601. });
  602. }
  603. catch (error) {
  604. console.error('Error deleting OTA task:', error);
  605. res.status(500).json({
  606. success: false,
  607. message: 'Failed to delete OTA task',
  608. error: error instanceof Error ? error.message : '未知错误'
  609. });
  610. }
  611. }
  612. static async bulkCreateOTATask(req, res) {
  613. try {
  614. const { firmwareId, deviceIds, retryCount = 3, retryInterval = 10000, timeout = 30000 } = req.body;
  615. if (!Array.isArray(deviceIds) || deviceIds.length === 0) {
  616. res.status(400).json({
  617. success: false,
  618. message: 'Device IDs must be a non-empty array',
  619. error: '设备ID必须是非空数组'
  620. });
  621. return;
  622. }
  623. const firmware = await firmware_1.FirmwareFileModel.getById(firmwareId);
  624. if (!firmware) {
  625. res.status(404).json({
  626. success: false,
  627. message: 'Firmware not found',
  628. error: '固件不存在'
  629. });
  630. return;
  631. }
  632. const validDevices = [];
  633. const invalidDevices = [];
  634. for (const deviceId of deviceIds) {
  635. const device = await device_1.DeviceModel.getByClientId(deviceId);
  636. if (device) {
  637. validDevices.push(deviceId);
  638. }
  639. else {
  640. invalidDevices.push(deviceId);
  641. }
  642. }
  643. if (validDevices.length === 0) {
  644. res.status(404).json({
  645. success: false,
  646. message: 'No valid devices found',
  647. error: '未找到有效设备'
  648. });
  649. return;
  650. }
  651. const createdTasks = [];
  652. const onlineTasks = [];
  653. const offlineTasks = [];
  654. const mqttBroker = mqttBrokerService_1.MqttBrokerService.getInstance();
  655. const otaServerUrl = process.env.OTA_SERVER_URL || process.env.BACKEND_URL || 'http://localhost:3002';
  656. for (const deviceId of validDevices) {
  657. const device = await device_1.DeviceModel.getByClientId(deviceId);
  658. const taskData = {
  659. device_id: deviceId,
  660. firmware_id: firmwareId,
  661. status: 'pending',
  662. progress: 0,
  663. start_time: new Date()
  664. };
  665. const task = await ota_1.OTATaskModel.create(taskData);
  666. createdTasks.push(task);
  667. if (device && device.status === 'online') {
  668. onlineTasks.push({ taskId: task.id, deviceId });
  669. const otaCommand = {
  670. act: 'upgrade',
  671. ver: firmware.version,
  672. url: `${otaServerUrl}/api/ota/firmware/${firmware.id}`,
  673. md5: firmware.md5sum,
  674. tid: task.id,
  675. rc: retryCount,
  676. ri: retryInterval,
  677. to: timeout
  678. };
  679. console.log('OTA升级指令(批量):', JSON.stringify(otaCommand, null, 2));
  680. console.log('MQTT Topic:', `device/${deviceId}/ota`);
  681. mqttBroker.publish(`device/${deviceId}/ota`, JSON.stringify(otaCommand));
  682. }
  683. else {
  684. offlineTasks.push({ taskId: task.id, deviceId });
  685. console.log(`设备 ${deviceId} 离线,OTA任务 ${task.id} 将在设备上线后自动执行`);
  686. }
  687. }
  688. loggerService_1.LoggerService.info('批量OTA任务创建完成', {
  689. source: 'ota',
  690. module: 'bulk_create_ota_task',
  691. details: JSON.stringify({
  692. totalTasks: createdTasks.length,
  693. onlineTasks: onlineTasks.length,
  694. offlineTasks: offlineTasks.length,
  695. invalidDevices: invalidDevices.length
  696. })
  697. }).catch(err => {
  698. console.error('批量OTA任务创建日志写入失败:', err);
  699. });
  700. res.status(201).json({
  701. success: true,
  702. message: `批量OTA任务创建成功(在线设备: ${onlineTasks.length}, 离线设备: ${offlineTasks.length})`,
  703. data: {
  704. tasks: createdTasks,
  705. validDevices: validDevices.length,
  706. invalidDevices: invalidDevices,
  707. onlineTasks: onlineTasks.length,
  708. offlineTasks: offlineTasks.length
  709. }
  710. });
  711. }
  712. catch (error) {
  713. console.error('Error creating bulk OTA tasks:', error);
  714. res.status(500).json({
  715. success: false,
  716. message: '批量OTA任务创建失败',
  717. error: error instanceof Error ? error.message : '未知错误'
  718. });
  719. }
  720. }
  721. }
  722. exports.OtaController = OtaController;
  723. //# sourceMappingURL=otaController.js.map