import 'package:classic_bluetooth/classic_bluetooth.dart'; import '/routes/app_routes.dart'; import 'package:logger/web.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:path/path.dart'; import 'dart:async'; import '../../../data/services/network/api.dart'; import '../model/ota_upgrade_model.dart'; import 'dart:io'; import 'package:dio/dio.dart'; import 'package:path_provider/path_provider.dart'; import 'package:ota/ota.dart'; import '../../../data/services/ble_manager.dart'; class OtaUpgradeController extends GetxController { static const int NOT_STARTED = -1; // 未开始 static const int VALIDATION_FILE = 0; // 验证文件 static const int ONUPGRADE = 1; // 升级中 static const int START_UPGRADING = 2; // 开始升级 static const int UPGRADE_SUCCESS = 3; // 升级成功 static const int UPGRADE_FAILED = 4; // 升级失败 static const int CONNECTION_DISCONNECTION = 5; // 连接断开 static const int CONNECTION_SUCCESS = 6; // 连接成功 static const int DOWNLOADING_UP_FILES = 7; // 下载升级文件 // 错误信息,用于存储错误提示 final RxString errorMessage = ''.obs; // 最新版本号 final RxString latestVersion = ''.obs; // 最新更新内容 final RxString updatedescription = ''.obs; // 当前设备固件版本号 final RxString currentVersion = ''.obs; String _address = ''; // 更新包地址,用于下载更新文件 String updatepackageaddress = ''; // 下载进度,范围为 0.0 到 1.0 final RxDouble downloadProgress = 0.0.obs; // 是否有可用更新的状态 final isUpdateAvailable = true.obs; // 当前升级进度,范围为 0.0 到 1.0 // final RxDouble progress = 0.0.obs; // OTA 单例实例,用于处理升级相关逻辑 final jlota = Ota.instance; // 是否正在下载更新包的状态 final RxBool isDownloading = false.obs; // 蓝牙管理服务,用于获取设备信息和通信 final _bleManager = Get.find(); final maxRetryAttempts = 3; // 最大重试次数 int currentRetryCount = 0; final RxBool isFetchingDeviceInfo = true.obs; final RxDouble progress = 0.0.obs; // 控制器自己的可观察属性 final RxInt type = OtaEvent.NOT_STARTED.obs; final RxBool isUpgrade = false.obs; // 新增:详细状态提示 final RxString detailedStatus = ''.obs; final RxBool showDetailedStatus = false.obs; @override void onInit() { super.onInit(); _getCurrentVersion(); } @override void onClose() { jlota.onOtaClose(); _bleManager.setEnterOta(false); isUpgrade.value = false; super.onClose(); } // 添加重试方法 void retryConnection() { errorMessage.value = ''; _getCurrentVersion(); } String getStatusMessage(int type) { switch (type) { case NOT_STARTED: // -1 return "未开始".tr; case VALIDATION_FILE: // 0 return "验证文件中".tr; case ONUPGRADE: // 1 return "升级中".tr; case START_UPGRADING: // 2 return "开始升级".tr; case UPGRADE_SUCCESS: // 3 return "升级成功".tr; case UPGRADE_FAILED: // 4 return "升级失败".tr; case CONNECTION_DISCONNECTION: // 5 return "连接断开".tr; case CONNECTION_SUCCESS: // 6 return "连接成功".tr; case DOWNLOADING_UP_FILES: // 7 (新增) return "下载升级文件".tr; default: return "未知状态".tr; // 更安全的默认值 } } // 添加更新方法 Future startUpdate() async { try { checkUpdate(); // 实现更新逻辑... } catch (e) { errorMessage.value = e.toString(); Get.snackbar( '更新失败', e.toString(), snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.red[600], colorText: Colors.white, icon: const Icon( Icons.error_outline, color: Colors.white, ), ); } } Future _getCurrentVersion() async { _listenToEvents(); try { isFetchingDeviceInfo.value = true; errorMessage.value = ''; // 添加获取设备信息的重试机制 DeviceInfo? deviceInfo = _bleManager.deviceInfo.value; // // 第一次检查设备信息 // if (deviceInfo == null) { // // 尝试重新获取设备信息 // await _bleManager.fetchDeviceInfo(); // } while (currentRetryCount < maxRetryAttempts) { deviceInfo = _bleManager.deviceInfo.value; // 检查信息有效性 if (deviceInfo != null && deviceInfo.firmwareVersion.isNotEmpty && deviceInfo.firmwareProductId.isNotEmpty) { break; } // 获取设备信息 await _bleManager.fetchDeviceInfo(); await Future.delayed(const Duration(seconds: 5)); currentRetryCount++; } // 最终验证 if (deviceInfo == null || deviceInfo.firmwareVersion.isEmpty || deviceInfo.firmwareProductId.isEmpty) { throw "获取设备信息失败,请检查连接"; } // 拆分 firmwareVersion 并判断两边是否一致 final firmwareParts = deviceInfo.firmwareVersion.split('.'); // 从第三个 `.` 开始分割 final firstPart = firmwareParts.sublist(0, 3).join('.'); final secondPart = firmwareParts.sublist(3).join('.'); Logger().d("firstPart${firstPart},secondPart=${secondPart}"); // 判断两部分是否一致 if (firstPart != secondPart) { throw "固件版本格式错误:${deviceInfo.firmwareVersion}"; } // 正常流程 currentVersion.value = firstPart; Logger().d('getequipmentinfo,id: ${deviceInfo.firmwareProductId}'); final connectedDevices = await ClassicBluetooth.getConnectedDevices(); if (connectedDevices.isNotEmpty) { _address = connectedDevices.first.address; } // API请求 final response = await Api.getproduct({ "id": deviceInfo.firmwareProductId, }); // 处理API响应 if (response['product'] == null) { throw const FormatException('API 响应格式错误'); } final resp = OtaConfigResp.fromJson(response); Logger().d('getequipmentinfo,resp.systemversion: ${resp.systemversion}'); latestVersion.value = resp.systemversion!; updatedescription.value = resp.updatedescription!; updatepackageaddress = resp.updatepackageaddress!; // 版本检查 bool isUpdate = _isNewVersionAvailable(resp.systemversion!); if (isUpdate) { await _bleManager.setEnterOta(true); await Future.delayed(const Duration(seconds: 2)); jlota.init(); } isUpdateAvailable.value = isUpdate; } catch (e) { // 统一错误处理 errorMessage.value = e.toString(); Get.snackbar( '错误', e.toString(), snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.red[600], colorText: Colors.white, icon: const Icon( Icons.error_outline, color: Colors.white, ), ); // 重要:重新抛出异常以便上层处理(可选) } finally { isFetchingDeviceInfo.value = false; } } // 版本比较方法优化 bool _isNewVersionAvailable(String latest) { try { List currentParts = currentVersion.value .split('.') .map((e) => int.parse(e.trim())) .toList(); List latestParts = latest.split('.').map((e) => int.parse(e.trim())).toList(); for (int i = 0; i < latestParts.length; i++) { int current = (i < currentParts.length) ? currentParts[i] : 0; if (latestParts[i] > current) return true; if (latestParts[i] < current) return false; } return false; } catch (e) { Logger().d('版本比较错误: $e'); return false; } } // 版本更新 Future checkUpdate() async { try { if (latestVersion.value != "") { _showUpdateDialog(latestVersion.value); } else { Get.snackbar( '错误', '获取最新版本信息失败', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.orange[800], colorText: Colors.white, icon: const Icon( Icons.error_outline, color: Colors.white, ), ); } } catch (e) { Get.snackbar( '错误', '获取版本信息失败', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.orange[800], colorText: Colors.white, icon: const Icon( Icons.error_outline, color: Colors.white, ), ); } } void _showUpdateDialog(String latest) async { Logger().d('显示更新对话框'); Get.dialog( AlertDialog( title: Text('发现新版本 $latest'), content: Text('是否立即更新?'), actions: [ TextButton( onPressed: () async { Logger().d('取消按钮被点击'); Get.back(); Future.delayed(Duration(milliseconds: 300)); }, child: Text('取消'), ), TextButton( onPressed: () async { Logger().d('更新按钮被点击'); Get.back(); Future.delayed(Duration(milliseconds: 300)); // 检查设备电量 if (!_checkBatteryLevel()) { return; // 电量不足,不执行升级 } startDownload(); }, child: Text('更新'), ), ], ), ); } // 修改后的startDownload方法 Future startDownload() async { isDownloading.value = true; showDetailedStatus.value = true; detailedStatus.value = '准备下载固件包...'; int lastIndex = updatepackageaddress.lastIndexOf('/'); String fileName = updatepackageaddress.substring(lastIndex + 1); String directoryName = "ota_upgrades"; final dio = Dio(BaseOptions( connectTimeout: const Duration(seconds: 5), receiveTimeout: Duration.zero, sendTimeout: const Duration(seconds: 5), headers: {'Accept-Encoding': 'gzip'}, )); type.value = DOWNLOADING_UP_FILES; try { detailedStatus.value = '正在获取下载目录...'; final Directory? downloadsDir = await getApplicationSupportDirectory(); if (downloadsDir == null) { throw FileSystemException('无法访问下载目录'); } final targetDir = Directory('${downloadsDir.path}/Download/$directoryName'); if (!await targetDir.exists()) { detailedStatus.value = '创建下载目录...'; await targetDir.create(recursive: true); } final savePath = '${targetDir.path}/$fileName'; // 新增:检查同名文件逻辑 detailedStatus.value = '检查本地文件...'; bool existingSameFile = false; try { var files = await targetDir.list().toList(); for (var entity in files) { if (await FileSystemEntity.isFile(entity.path)) { String name = basename(entity.path); // 使用path包中的basename if (name == fileName) { existingSameFile = true; break; } } } } catch (e) { Get.snackbar( '错误', '检查本地文件失败: $e', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.red[600], colorText: Colors.white, icon: Icon( Icons.error_outline, color: Colors.white, ), ); return; } if (existingSameFile) { //Get.snackbar('文件已存在', '跳过下载,直接更新'); detailedStatus.value = '文件已存在,准备升级...'; startUpgrading(savePath); return; } // 新增:删除其他文件逻辑 detailedStatus.value = '清理旧文件...'; try { var files = await targetDir.list().toList(); for (var entity in files) { if (await FileSystemEntity.isFile(entity.path)) { await entity.delete(); } } } catch (e) { Get.snackbar( '错误', '删除旧文件失败: $e', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.red[600], colorText: Colors.white, icon: Icon( Icons.error_outline, color: Colors.white, ), ); //isUpgrade.value = false; return; } Logger().d('文件保存路径: $savePath'); detailedStatus.value = '开始下载固件包...'; final options = Options( receiveTimeout: Duration.zero, followRedirects: true, maxRedirects: 5, ); // 修改下载部分 await dio.download( updatepackageaddress, savePath, options: options, onReceiveProgress: (received, total) { if (total != -1) { downloadProgress.value = received / total; detailedStatus.value = '正在下载固件包... ${(received / total * 100).toStringAsFixed(1)}%'; } }, ); File file = File(savePath); if (await file.exists()) { // Get.snackbar('下载完成', '文件已保存至: $savePath'); detailedStatus.value = '下载完成,准备升级...'; } else { Get.snackbar( '错误', '文件未找到,请检查写入权限', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.orange[600], colorText: Colors.white, icon: Icon( Icons.error_outline, color: Colors.white, ), ); detailedStatus.value = '下载失败'; showDetailedStatus.value = false; return; } isDownloading.value = false; startUpgrading(savePath); return; } on DioException catch (e) { Get.snackbar( '下载失败', '错误: ${e.message}', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.orange[600], colorText: Colors.white, icon: Icon( Icons.error_outline, color: Colors.white, ), ); } on FileSystemException catch (e) { Get.snackbar( '存储失败', '无法写入文件: ${e.message}', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.orange[600], colorText: Colors.white, icon: Icon( Icons.error_outline, color: Colors.white, ), ); } catch (e) { isDownloading.value = false; detailedStatus.value = '下载失败,请检查网络连接'; showDetailedStatus.value = false; Get.snackbar( '下载失败', '请检查网络连接: $e', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.orange[600], colorText: Colors.white, icon: Icon( Icons.error_outline, color: Colors.white, ), ); Logger().d('下载失败: $e'); } } // 开始升级 void startUpgrading(String path) async { detailedStatus.value = '准备设备连接...'; await Future.delayed(const Duration(milliseconds: 500)); detailedStatus.value = '正在连接设备...'; jlota.startUpgrading(address: _address, filePath: path); return; // isUpgrade.value = false; } // 停止升级 void stopUpgrading() async { jlota.onOtaClose(); return; // isUpgrade.value = false; } // 检查设备电量是否满足升级条件 bool _checkBatteryLevel() { try { final deviceInfo = _bleManager.deviceInfo.value; if (deviceInfo == null) { Get.snackbar( '电量检查失败', '无法获取设备信息,请检查设备连接', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.orange[600], colorText: Colors.white, icon: Icon( Icons.warning_amber, color: Colors.white, ), ); return false; } final leftBattery = deviceInfo.leftEarBattery; final rightBattery = deviceInfo.rightEarBattery; // 检查左右耳电量是否都大于30% if (leftBattery <= 30 || rightBattery <= 30) { String message = '设备电量不足,无法进行固件升级\n'; message += '升级要求:左右耳电量均需大于30%\n'; message += '当前电量:左耳 ${leftBattery}%,右耳 ${rightBattery}%\n'; message += '请充电后再试'; Get.dialog( AlertDialog( title: Text( '电量不足', style: TextStyle( color: Colors.orange[700], fontWeight: FontWeight.w600, ), ), content: Text( message, style: TextStyle( color: Colors.orange[600], ), ), actions: [ TextButton( onPressed: () => Get.back(), style: TextButton.styleFrom( foregroundColor: Colors.orange[700], ), child: const Text('确定'), ), ], shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide( color: Colors.orange.withValues(alpha: 0.3), width: 1, ), ), backgroundColor: Colors.orange[50], ), ); return false; } return true; } catch (e) { Get.snackbar( '电量检查失败', '检查设备电量时发生错误:$e', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.orange[600], colorText: Colors.white, icon: const Icon( Icons.warning_amber, color: Colors.white, ), ); return false; } } void _listenToEvents() { Ota.instance.eventStream.listen((event) { if (event.event == 'onProgress' && event.type != null && event.progress != null) { progress.value = event.progress!; type.value = event.type!; isUpgrade.value = true; // 开始升级 detailedStatus.value = '升级进度: ${(event.progress! * 100).toStringAsFixed(1)}%'; } if (event.event == 'onStartOTA') { type.value = START_UPGRADING; isUpgrade.value = true; // 开始升级 detailedStatus.value = 'OTA升级已开始...'; } if (event.event == 'onStopOTA') { type.value = UPGRADE_SUCCESS; isUpgrade.value = false; detailedStatus.value = '升级成功完成'; showDetailedStatus.value = false; // 升级成功,更新版本信息显示 currentVersion.value = latestVersion.value; isUpdateAvailable.value = false; // 显示升级成功对话框,引导用户重新配对 Get.dialog( AlertDialog( title: Row( children: [ Icon( Icons.check_circle, color: Colors.green[600], size: 28, ), SizedBox(width: 12), Text( '升级成功', style: TextStyle( color: Colors.green[700], fontWeight: FontWeight.w600, ), ), ], ), content: Text( '固件升级已完成!\n\n设备需要重新配对才能正常使用,现在跳转到配对页面吗?', style: TextStyle( fontSize: 16, height: 1.4, ), ), actions: [ TextButton( onPressed: () { Get.back(); // 关闭对话框 }, child: Text( '稍后配对', style: TextStyle(color: Colors.grey[600]), ), ), ElevatedButton( onPressed: () { Get.back(); // 关闭对话框 Get.toNamed(Routes.pairing); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.green[600], foregroundColor: Colors.white, ), child: Text('立即配对'), ), ], shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), barrierDismissible: false, // 防止用户意外关闭 ); } if (event.event == 'onError') { type.value = UPGRADE_FAILED; isUpgrade.value = false; detailedStatus.value = '升级失败,错误码:${event.code}'; showDetailedStatus.value = false; Get.snackbar( '升级失败', '请检查设备连接或升级包,错误码:${event.code}', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.red[600], colorText: Colors.white, icon: Icon( Icons.error_outline, color: Colors.white, ), ); } if (event.event == 'NOCONNECTION') { type.value = CONNECTION_DISCONNECTION; isUpgrade.value = false; detailedStatus.value = '设备连接断开'; showDetailedStatus.value = false; } if (event.event == 'CONNECTION_SUCCESS') { type.value = CONNECTION_SUCCESS; isUpgrade.value = true; // 开始升级 detailedStatus.value = '设备连接成功'; } }); } }