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.

416 lines
12 KiB

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 currentVersion = ''.obs;
// 更新包地址,用于下载更新文件
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<BleManager>();
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;
@override
void onInit() {
super.onInit();
_getCurrentVersion();
}
@override
void onClose() {
jlota.onOtaClose();
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<void> startUpdate() async {
try {
checkUpdate();
// 实现更新逻辑...
} catch (e) {
errorMessage.value = e.toString();
Get.snackbar('更新失败', e.toString());
}
}
Future<void> _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('.');
print("firstPart${firstPart},secondPart=${secondPart}");
// 判断两部分是否一致
if (firstPart != secondPart) {
throw "固件版本格式错误:${deviceInfo.firmwareVersion}";
}
// 正常流程
currentVersion.value = firstPart;
print('getequipmentinfo,id: ${deviceInfo.firmwareProductId}');
// API请求
final response = await Api.getequipmentinfo({
"id": deviceInfo.firmwareProductId,
});
// 处理API响应
if (response['info'] == null) {
throw const FormatException('API 响应格式错误');
}
final resp = OtaConfigResp.fromJson(response);
print('getequipmentinfo,resp.systemversion: ${resp.systemversion}');
latestVersion.value = resp.systemversion!;
updatepackageaddress = resp.updatepackageaddress!;
// 版本检查
isUpdateAvailable.value = _isNewVersionAvailable(resp.systemversion!);
} catch (e) {
// 统一错误处理
errorMessage.value = e.toString();
Get.snackbar('错误', e.toString(),
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.red,
colorText: Colors.white);
// 重要:重新抛出异常以便上层处理(可选)
} finally {
isFetchingDeviceInfo.value = false;
}
}
// 版本比较方法优化
bool _isNewVersionAvailable(String latest) {
try {
List<int> currentParts = currentVersion.value
.split('.')
.map((e) => int.parse(e.trim()))
.toList();
List<int> 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) {
print('版本比较错误: $e');
return false;
}
}
// 版本更新
Future<void> checkUpdate() async {
try {
if (latestVersion.value != "") {
_showUpdateDialog(latestVersion.value);
} else {
Get.snackbar('错误', '获取最新版本信息失败');
}
} catch (e) {
Get.snackbar('错误', '获取版本信息失败');
}
}
void _showUpdateDialog(String latest) async {
print('显示更新对话框');
Get.dialog(
AlertDialog(
title: Text('发现新版本 $latest'),
content: Text('是否立即更新?'),
actions: [
TextButton(
onPressed: () async {
print('取消按钮被点击');
Get.back();
Future.delayed(Duration(milliseconds: 300));
},
child: Text('取消'),
),
TextButton(
onPressed: () async {
print('更新按钮被点击');
Get.back();
Future.delayed(Duration(milliseconds: 300));
startDownload();
},
child: Text('更新'),
),
],
),
);
}
// 修改后的startDownload方法
Future<void> startDownload() async {
isDownloading.value = true;
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 {
final Directory? downloadsDir = await getApplicationSupportDirectory();
if (downloadsDir == null) {
throw FileSystemException('无法访问下载目录');
}
final targetDir =
Directory('${downloadsDir.path}/Download/$directoryName');
if (!await targetDir.exists()) {
await targetDir.create(recursive: true);
}
final savePath = '${targetDir.path}/$fileName';
// 新增:检查同名文件逻辑
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');
return;
}
if (existingSameFile) {
Get.snackbar('文件已存在', '跳过下载,直接更新');
startUpgrading(savePath);
return;
}
// 新增:删除其他文件逻辑
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');
//isUpgrade.value = false;
return;
}
print('文件保存路径: $savePath');
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;
}
},
);
File file = File(savePath);
if (await file.exists()) {
Get.snackbar('下载完成', '文件已保存至: $savePath');
} else {
Get.snackbar('错误', '文件未找到,请检查写入权限');
}
isDownloading.value = false;
startUpgrading(savePath);
return;
} on DioException catch (e) {
Get.snackbar('下载失败', '错误: ${e.message}');
} on FileSystemException catch (e) {
Get.snackbar('存储失败', '无法写入文件: ${e.message}');
} catch (e) {
Get.snackbar('未知错误', e.toString());
}
}
// 开始升级
void startUpgrading(String path) async {
jlota.startUpgrading(address: 'E9:09:05:E0:18:7A', filePath: path);
return;
// isUpgrade.value = false;
}
// 停止升级
void stopUpgrading() async {
jlota.onOtaClose();
return;
// isUpgrade.value = 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; // 开始升级
}
if (event.event == 'onStartOTA') {
type.value = OtaEvent.START_UPGRADING;
isUpgrade.value = true; // 开始升级
}
if (event.event == 'onStopOTA') {
type.value = OtaEvent.UPGRADE_SUCCESS;
isUpgrade.value = false;
Get.snackbar('升级成功', '请退出重新连接设备');
_getCurrentVersion();
}
if (event.event == 'onError') {
type.value = OtaEvent.UPGRADE_FAILED;
isUpgrade.value = false;
}
if (event.event == 'NOCONNECTION') {
type.value = OtaEvent.CONNECTION_DISCONNECTION;
isUpgrade.value = false;
}
if (event.event == 'CONNECTION_SUCCESS') {
type.value = OtaEvent.CONNECTION_SUCCESS;
isUpgrade.value = true; // 开始升级
}
});
}
}