You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
363 lines
12 KiB
363 lines
12 KiB
import 'dart:async';
|
|
|
|
import 'package:classic_bluetooth/classic_bluetooth.dart';
|
|
import 'package:device_jieli/device_jieli.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
// import 'package:logger/web.dart';
|
|
import 'package:get_storage/get_storage.dart';
|
|
import '../../../data/services/ble_manager.dart';
|
|
import 'ota_core_contropller.dart';
|
|
import 'ota_download_contropller.dart';
|
|
import '../views/ota_upgrade_popup_view.dart';
|
|
import '../../../core/utils/logger.dart';
|
|
|
|
/// OTA升级主控制器 - 重构后的简化版本
|
|
/// 负责协调各个服务,管理升级流程的整体状态
|
|
class OtaUpgradeController extends GetxController {
|
|
// 服务依赖 - 使用新的合并后的服务
|
|
final _coreService = OtaCoreService();
|
|
final _downloadService = OtaDownloadService();
|
|
final _uiService = OtaUpgradePopUp();
|
|
final _bleManager = Get.find<BleManager>();
|
|
final _jlota = Jielihome.instance;
|
|
StreamSubscription<JieliEvent>? _otaEventSub;
|
|
|
|
// 状态管理
|
|
final RxString errorMessage = ''.obs;
|
|
final RxString latestVersion = ''.obs;
|
|
final RxString currentVersion = ''.obs;
|
|
final RxString detailedStatus = ''.obs;
|
|
final RxString updatedescription = ''.obs;
|
|
final RxDouble downloadProgress = 0.0.obs;
|
|
final RxDouble progress = 0.0.obs;
|
|
final RxInt type = OtaCoreService.NOT_STARTED.obs;
|
|
final RxBool isUpdateAvailable = false.obs;
|
|
final RxBool isDownloading = false.obs;
|
|
final RxBool isUpgrade = false.obs;
|
|
final RxBool isFetchingDeviceInfo = false.obs;
|
|
final RxBool showDetailedStatus = false.obs;
|
|
final RxBool isBleConnected = true.obs;
|
|
final GetStorage _storage = GetStorage();
|
|
|
|
String _address = '';
|
|
String _updatePackageAddress = '';
|
|
String? _lastConnectedDeviceImage; // 新增:存储上一次连接设备的图片链接
|
|
|
|
get lastConnectedDeviceImage => _lastConnectedDeviceImage;
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
_loadLastConnectedDeviceImage(); // 加载持久化的设备图片
|
|
_initializeController();
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
_jlota.otaCancel();
|
|
_otaEventSub?.cancel();
|
|
_otaEventSub = null;
|
|
_bleManager.setEnterOta(false);
|
|
isUpgrade.value = false;
|
|
super.onClose();
|
|
}
|
|
|
|
/// 从本地存储加载上一次连接设备的图片链接
|
|
void _loadLastConnectedDeviceImage() {
|
|
_lastConnectedDeviceImage = _storage.read('last_connected_device_image');
|
|
Logger.d(
|
|
'OtaUpgradeController', '加载上一次连接设备的图片: $_lastConnectedDeviceImage');
|
|
}
|
|
|
|
/// 初始化控制器
|
|
/// 设置事件监听并加载版本信息
|
|
Future<void> _initializeController() async {
|
|
// 首先检查BLE设备连接状态
|
|
if (!_coreService.checkBleConnectionStatus()) {
|
|
//isFetchingDeviceInfo.value = false;
|
|
//errorMessage.value = 'unconnected'.tr;
|
|
isBleConnected.value = false;
|
|
|
|
return;
|
|
}
|
|
_listenToOtaEvents();
|
|
await _loadVersionInfo();
|
|
}
|
|
|
|
/// 加载版本信息
|
|
/// 获取当前设备版本和最新版本信息,检查是否有更新
|
|
Future<void> _loadVersionInfo() async {
|
|
try {
|
|
isFetchingDeviceInfo.value = true;
|
|
errorMessage.value = '';
|
|
|
|
// 获取当前版本信息
|
|
final currentInfo = await _coreService.getCurrentVersionInfo();
|
|
currentVersion.value = currentInfo['currentVersion'];
|
|
|
|
// 获取蓝牙地址
|
|
final connectedDevices = await ClassicBluetooth.getConnectedDevices();
|
|
if (connectedDevices.isNotEmpty) {
|
|
_address = connectedDevices.first.address;
|
|
}
|
|
|
|
// 获取最新版本信息
|
|
final latestInfo = await _coreService.getLatestVersionInfo();
|
|
latestVersion.value = latestInfo['latestVersion'];
|
|
_updatePackageAddress = latestInfo['updatePackageAddress'];
|
|
|
|
// 设置更新描述信息
|
|
updatedescription.value =
|
|
latestInfo['updateDescription'] ?? 'otaDefaultUpdateDesc'.tr;
|
|
|
|
// 检查是否有更新
|
|
isUpdateAvailable.value = _coreService.isNewVersionAvailable(
|
|
currentVersion.value,
|
|
latestVersion.value,
|
|
);
|
|
} catch (e) {
|
|
_handleError('error'.tr, _getUserFriendlyErrorMessage(e));
|
|
} finally {
|
|
isFetchingDeviceInfo.value = false;
|
|
}
|
|
}
|
|
|
|
/// 检查更新
|
|
/// 显示更新确认对话框或错误提示
|
|
Future<void> checkUpdate() async {
|
|
try {
|
|
if (latestVersion.value.isNotEmpty) {
|
|
_uiService.showUpdateDialog(latestVersion.value, _startUpdateProcess);
|
|
} else {
|
|
_uiService.showErrorSnackbar('error'.tr, 'otaGetLatestVersionFailed'.tr,
|
|
backgroundColor: Colors.orange[800]);
|
|
}
|
|
} catch (e) {
|
|
_uiService.showErrorSnackbar('error'.tr, 'otaGetVersionInfoFailed'.tr,
|
|
backgroundColor: Colors.orange[800]);
|
|
}
|
|
}
|
|
|
|
/// 开始更新流程的公共方法
|
|
/// 供视图调用的更新入口点
|
|
Future<void> startUpdate() async {
|
|
//立即给用户状态反馈
|
|
showDetailedStatus.value = true;
|
|
detailedStatus.value = 'otaCheckingDeviceStatus'.tr;
|
|
|
|
await _startUpdateProcess();
|
|
}
|
|
|
|
/// 检查设备状态后开始下载和升级
|
|
Future<void> _startUpdateProcess() async {
|
|
// 检查设备电量
|
|
if (!_coreService.checkBatteryLevel()) {
|
|
return;
|
|
}
|
|
|
|
await _startDownload();
|
|
}
|
|
|
|
/// 开始下载固件
|
|
/// 初始化OTA环境并下载固件包
|
|
Future<void> _startDownload() async {
|
|
try {
|
|
await _bleManager.setEnterOta(true);
|
|
await Future.delayed(const Duration(seconds: 2));
|
|
// device_jieli 已经在全局 InitialBinding 里 initialize 过;
|
|
// 这里无需重复 init —— 直接订阅 OTA 事件流即可。
|
|
|
|
isDownloading.value = true;
|
|
showDetailedStatus.value = true;
|
|
type.value = OtaCoreService.DOWNLOADING_UP_FILES;
|
|
|
|
final filePath = await _downloadService.downloadFirmware(
|
|
downloadUrl: _updatePackageAddress,
|
|
onProgress: (progress) {
|
|
downloadProgress.value = progress;
|
|
},
|
|
onStatusUpdate: (status) {
|
|
detailedStatus.value = status;
|
|
},
|
|
);
|
|
|
|
isDownloading.value = false;
|
|
await _startUpgrading(filePath);
|
|
} catch (e) {
|
|
isDownloading.value = false;
|
|
detailedStatus.value = 'otaDownloadFailedCheckNetwork'.tr;
|
|
showDetailedStatus.value = false;
|
|
_handleError('otaDownloadFailed'.tr, e.toString());
|
|
Logger.d('OtaUpgradeController', '下载失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 开始升级
|
|
/// [filePath] 固件文件路径
|
|
/// 启动OTA升级过程
|
|
Future<void> _startUpgrading(String filePath) async {
|
|
detailedStatus.value = 'otaPreparingConnection'.tr;
|
|
await Future.delayed(const Duration(milliseconds: 500));
|
|
detailedStatus.value = 'otaConnectingDevice'.tr;
|
|
_jlota.otaStart(address: _address, firmwareFilePath: filePath);
|
|
}
|
|
|
|
/// 停止升级
|
|
/// 关闭OTA连接
|
|
void stopUpgrading() {
|
|
_jlota.otaCancel();
|
|
}
|
|
|
|
/// 重试连接
|
|
/// 清除错误信息并重新加载版本信息
|
|
void retryConnection() {
|
|
errorMessage.value = '';
|
|
_loadVersionInfo();
|
|
}
|
|
|
|
/// 获取状态描述
|
|
/// [type] 状态类型
|
|
/// 返回对应的状态描述文本
|
|
String getStatusMessage(int type) {
|
|
return OtaCoreService.getStatusMessage(type);
|
|
}
|
|
|
|
/// 监听OTA事件
|
|
/// 订阅 device_jieli 的统一事件流,过滤 OTA 相关事件并转发到分发器。
|
|
void _listenToOtaEvents() {
|
|
_otaEventSub?.cancel();
|
|
_otaEventSub = _jlota.events.listen(_handleOtaEvent);
|
|
}
|
|
|
|
/// 处理 device_jieli 抛出的 OTA / 连接事件。
|
|
/// - [OtaStateEvent]: 进度 / 状态机推进
|
|
/// - [OtaErrorEvent]: 升级失败
|
|
/// - [ConnectionStateEvent]: 设备连接 / 断开(OTA 期间用于"中断"指示)
|
|
void _handleOtaEvent(JieliEvent event) {
|
|
if (event is OtaStateEvent) {
|
|
switch (event.state) {
|
|
case OtaState.entering:
|
|
case OtaState.inquiring:
|
|
case OtaState.notifyingSize:
|
|
type.value = OtaCoreService.START_UPGRADING;
|
|
isUpgrade.value = true;
|
|
detailedStatus.value = 'otaUpgradeStarted'.tr;
|
|
break;
|
|
case OtaState.transferring:
|
|
if (event.percent >= 0) {
|
|
// device_jieli 发的是 0..100 整数;UI 这边按 0..1 浮点用。
|
|
progress.value = event.percent / 100.0;
|
|
type.value = OtaCoreService.ONUPGRADE;
|
|
isUpgrade.value = true;
|
|
detailedStatus.value = 'otaUpgradeProgressWithPercent'
|
|
.trParams({'percent': event.percent.toStringAsFixed(1)});
|
|
}
|
|
break;
|
|
case OtaState.verifying:
|
|
detailedStatus.value = 'otaUpgradeStarted'.tr;
|
|
break;
|
|
case OtaState.rebooting:
|
|
// 设备重启等待,仍在升级中
|
|
isUpgrade.value = true;
|
|
break;
|
|
case OtaState.done:
|
|
_handleUpgradeSuccess();
|
|
break;
|
|
case OtaState.failed:
|
|
_handleUpgradeError(-1);
|
|
break;
|
|
case OtaState.cancelled:
|
|
isUpgrade.value = false;
|
|
showDetailedStatus.value = false;
|
|
break;
|
|
case OtaState.idle:
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
if (event is OtaErrorEvent) {
|
|
_handleUpgradeError(event.code);
|
|
return;
|
|
}
|
|
if (event is ConnectionStateEvent) {
|
|
if (event.state == ConnectionStateEvent.connectionDisconnect &&
|
|
isUpgrade.value) {
|
|
_handleConnectionLost();
|
|
} else if (event.state == ConnectionStateEvent.connectionOk &&
|
|
isUpgrade.value) {
|
|
_handleConnectionSuccess();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 处理升级成功
|
|
/// 更新状态并显示成功对话框
|
|
void _handleUpgradeSuccess() {
|
|
type.value = OtaCoreService.UPGRADE_SUCCESS;
|
|
isUpgrade.value = false;
|
|
detailedStatus.value = 'otaUpgradeSuccessComplete'.tr;
|
|
showDetailedStatus.value = false;
|
|
|
|
// 更新版本信息
|
|
currentVersion.value = latestVersion.value;
|
|
isUpdateAvailable.value = false;
|
|
|
|
// 显示成功对话框
|
|
_uiService.showUpgradeSuccessDialog();
|
|
}
|
|
|
|
/// 处理升级错误
|
|
/// [errorCode] 错误代码
|
|
/// 更新状态并显示错误提示
|
|
void _handleUpgradeError(dynamic errorCode) {
|
|
type.value = OtaCoreService.UPGRADE_FAILED;
|
|
isUpgrade.value = false;
|
|
detailedStatus.value = 'otaUpgradeFailedWithCode'.trParams({'code': errorCode.toString()});
|
|
showDetailedStatus.value = false;
|
|
_uiService.showErrorSnackbar(
|
|
'otaUpgradeFailedTitle'.tr,
|
|
'otaUpgradeFailedDesc'.trParams({'code': errorCode.toString()}),
|
|
);
|
|
}
|
|
|
|
/// 处理连接断开
|
|
/// 更新连接状态
|
|
void _handleConnectionLost() {
|
|
type.value = OtaCoreService.CONNECTION_DISCONNECTION;
|
|
isUpgrade.value = false;
|
|
detailedStatus.value = 'otaDeviceDisconnected'.tr;
|
|
showDetailedStatus.value = false;
|
|
}
|
|
|
|
/// 处理连接成功
|
|
/// 更新连接状态
|
|
void _handleConnectionSuccess() {
|
|
type.value = OtaCoreService.CONNECTION_SUCCESS;
|
|
isUpgrade.value = true;
|
|
detailedStatus.value = 'otaDeviceConnected'.tr;
|
|
}
|
|
|
|
/// 统一错误处理
|
|
/// [title] 错误标题
|
|
/// [message] 错误信息
|
|
/// 设置错误状态并显示错误提示
|
|
void _handleError(String title, String message) {
|
|
errorMessage.value = message;
|
|
_uiService.showErrorSnackbar(title, message);
|
|
}
|
|
|
|
String _getUserFriendlyErrorMessage(Object error) {
|
|
if (error is OtaCoreException) {
|
|
switch (error.type) {
|
|
case OtaCoreErrorType.deviceInfoUnavailable:
|
|
return 'otaDeviceInfoUnavailable'.tr;
|
|
case OtaCoreErrorType.productInfoUnavailable:
|
|
return 'otaProductInfoUnavailable'.tr;
|
|
case OtaCoreErrorType.firmwareVersionFormatInvalid:
|
|
return 'otaFirmwareVersionInvalid'.tr;
|
|
}
|
|
}
|
|
return 'otaVersionInfoFailed'.tr;
|
|
}
|
|
}
|
|
|