更新太和e护配置wifi失败问题
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:easydevice/easydevice.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
|
||||
import 'package:vbvs_app/component/tool/cmd.dart';
|
||||
import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart';
|
||||
import 'package:ef/ef.dart'; // THapp 所在的包
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:vbvs_app/component/tool/NewTopSlideNotification.dart';
|
||||
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
|
||||
import 'package:vbvs_app/enum/APPDeviceUpgrade.dart';
|
||||
import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart';
|
||||
import 'package:vbvs_app/pages/mh_page/device/upgrade/tool/device_upgrade_tool.dart';
|
||||
|
||||
// 固件升级配置(区分平台)
|
||||
class UpgradeConfig {
|
||||
@@ -70,7 +71,7 @@ class MultiDeviceFirmwareUpdater {
|
||||
|
||||
final MHTBlueToothController _btController = Get.find();
|
||||
|
||||
final Map<String, UpgradeTask> _upgradeTasks = {};
|
||||
final Map<String, UpgradeTask> upgradeTasks = {};
|
||||
final Map<String, Uint8List> _firmwareDataCache = {};
|
||||
final int _maxConcurrentUpgrades = 2;
|
||||
int _currentConcurrentUpgrades = 0;
|
||||
@@ -80,8 +81,8 @@ class MultiDeviceFirmwareUpdater {
|
||||
required String mac,
|
||||
required String firmwareUrl,
|
||||
}) async {
|
||||
if (_upgradeTasks.containsKey(mac)) {
|
||||
final existingTask = _upgradeTasks[mac]!;
|
||||
if (upgradeTasks.containsKey(mac)) {
|
||||
final existingTask = upgradeTasks[mac]!;
|
||||
if (existingTask.status == 'upgrading' ||
|
||||
existingTask.status == 'downloading') {
|
||||
throw Exception("[蓝牙指令执行日志] 设备 $mac 正在升级中".tr);
|
||||
@@ -96,7 +97,7 @@ class MultiDeviceFirmwareUpdater {
|
||||
startTime: DateTime.now(),
|
||||
);
|
||||
|
||||
_upgradeTasks[mac] = task;
|
||||
upgradeTasks[mac] = task;
|
||||
_updateUiProgress(mac, 0, 'waiting');
|
||||
|
||||
_processUpgradeQueue(thapp);
|
||||
@@ -106,7 +107,7 @@ class MultiDeviceFirmwareUpdater {
|
||||
|
||||
void _processUpgradeQueue(THapp thapp) {
|
||||
final waitingTasks =
|
||||
_upgradeTasks.values.where((task) => task.status == 'waiting').toList();
|
||||
upgradeTasks.values.where((task) => task.status == 'waiting').toList();
|
||||
|
||||
for (final task in waitingTasks) {
|
||||
if (_currentConcurrentUpgrades < _maxConcurrentUpgrades) {
|
||||
@@ -123,7 +124,10 @@ class MultiDeviceFirmwareUpdater {
|
||||
if (!task.thapp.isConnected) {
|
||||
throw Exception("[蓝牙指令执行日志] 设备未连接".tr);
|
||||
}
|
||||
|
||||
task.thapp.logingStream.listen((log) {
|
||||
ef.log("升级日志: $log");
|
||||
});
|
||||
FlutterBluePlus.setLogLevel(LogLevel.none);
|
||||
if (!_firmwareDataCache.containsKey(task.mac)) {
|
||||
await _downloadFirmware(task);
|
||||
}
|
||||
@@ -144,9 +148,11 @@ class MultiDeviceFirmwareUpdater {
|
||||
task.endTime = DateTime.now();
|
||||
_updateUiProgress(task.mac, 100, 'completed');
|
||||
|
||||
TopSlideNotification.show(Get.context!,
|
||||
NewTopSlideNotification.show(
|
||||
text: "设备 ${task.mac} 固件升级成功".tr, textColor: Colors.green);
|
||||
|
||||
MHTBlueToothController mhtBlueToothController = Get.find();
|
||||
mhtBlueToothController.localUpgradeMac.remove(task.mac);
|
||||
mhtBlueToothController.updateAll();
|
||||
task.completer.complete();
|
||||
} catch (e, stack) {
|
||||
ef.log("[蓝牙指令执行日志] 设备 ${task.mac} 升级失败: $e\n$stack");
|
||||
@@ -186,69 +192,68 @@ class MultiDeviceFirmwareUpdater {
|
||||
}
|
||||
}
|
||||
|
||||
// Future<void> _sendFirmwareFrames(UpgradeTask task) async {
|
||||
// final firmwareData = _firmwareDataCache[task.mac];
|
||||
// if (firmwareData == null) {
|
||||
// throw Exception("[蓝牙指令执行日志] 固件数据为空".tr);
|
||||
// }
|
||||
|
||||
// final totalFrames =
|
||||
// (firmwareData.length / UpgradeConfig.maxFrameSize).ceil();
|
||||
// ef.log("[蓝牙指令执行日志] 固件总帧数:$totalFrames");
|
||||
// final stopwatch = Stopwatch()..start();
|
||||
// DateTime lastFrameTime = DateTime.now();
|
||||
|
||||
// for (int sentFrames = 0; sentFrames < totalFrames; sentFrames++) {
|
||||
// if (task.status == 'cancelled') {
|
||||
// throw Exception("[蓝牙指令执行日志] 升级已取消".tr);
|
||||
// }
|
||||
|
||||
// final elapsed = stopwatch.elapsed;
|
||||
// final frameInterval = UpgradeConfig.getFrameInterval(elapsed);
|
||||
// final now = DateTime.now();
|
||||
|
||||
// if (now.difference(lastFrameTime) < frameInterval) {
|
||||
// await Future.delayed(Duration(milliseconds: 10));
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// final offset = sentFrames * UpgradeConfig.maxFrameSize;
|
||||
// final end = offset + UpgradeConfig.maxFrameSize;
|
||||
// final frameData = firmwareData.sublist(
|
||||
// offset,
|
||||
// end > firmwareData.length ? firmwareData.length : end,
|
||||
// );
|
||||
|
||||
// final base64Data = base64Encode(frameData);
|
||||
// final mmxCommand =
|
||||
// '''mmx path="/root/mtd/update" write base64 len=${frameData.length} offset=$offset data="$base64Data"''';
|
||||
|
||||
// try {
|
||||
// await _sendCommand(task.thapp, mmxCommand);
|
||||
// await Future.delayed(Duration(milliseconds: sentFrames<500?100:10));
|
||||
// } catch (e, stack) {
|
||||
// ef.log("[蓝牙指令执行日志] 发送固件帧失败: $e\n$stack");
|
||||
// rethrow;
|
||||
// }
|
||||
|
||||
// final waitTime = frameInterval - now.difference(lastFrameTime);
|
||||
// if (waitTime > Duration.zero) await Future.delayed(waitTime);
|
||||
|
||||
// final progress = ((sentFrames + 1) / totalFrames * 100).round();
|
||||
// _updateUiProgress(task.mac, progress, 'upgrading');
|
||||
|
||||
// lastFrameTime = now;
|
||||
// }
|
||||
|
||||
// stopwatch.stop();
|
||||
// }
|
||||
|
||||
Future<void> _sendFirmwareFrames(UpgradeTask task) async {
|
||||
final firmwareData = _firmwareDataCache[task.mac];
|
||||
if (firmwareData == null) {
|
||||
throw Exception("[蓝牙指令执行日志] 固件数据为空".tr);
|
||||
}
|
||||
|
||||
try {
|
||||
int _lastLoggedProgress = -1;
|
||||
ef.log("[蓝牙指令执行日志] 开始执行 OTA 升级流程");
|
||||
|
||||
// 调用 THapp 封装的 otaStart 方法
|
||||
int result = await task.thapp.otaStart(
|
||||
firmwareData,
|
||||
chunkSize: UpgradeConfig.maxFrameSize, // 单帧大小
|
||||
retryPerChunk: 2, // 每帧重试次数
|
||||
timeoutMs: 3000, // 每帧超时时间
|
||||
firststepsframes: 300, // 前300帧延时发送,防止过载
|
||||
firststepdelayms: 30, // 前300帧延迟 30ms
|
||||
onceDelayms: 10, // 之后帧间延迟
|
||||
disableBlog: true, // 升级时关闭日志
|
||||
withResponse: false, // 一般OTA不需要响应确认
|
||||
binmode: false, // 使用文本命令模式发送(mmx命令)
|
||||
onProgress: (double progress) {
|
||||
// 更新 UI 进度
|
||||
final percent = (progress * 100).clamp(0, 100).round();
|
||||
_updateUiProgress(task.mac, percent, 'upgrading');
|
||||
if (percent != _lastLoggedProgress) {
|
||||
ef.log("[升级进度]: ${percent}%");
|
||||
}
|
||||
_lastLoggedProgress = percent;
|
||||
},
|
||||
);
|
||||
|
||||
// 根据返回码判断状态
|
||||
switch (result) {
|
||||
case 0:
|
||||
ef.log("[蓝牙指令执行日志] OTA 升级完成 ✅");
|
||||
_updateUiProgress(task.mac, 100, 'completed');
|
||||
break;
|
||||
case -1:
|
||||
throw Exception("固件为空或格式错误");
|
||||
case -2:
|
||||
throw Exception("数据发送失败");
|
||||
case -3:
|
||||
throw Exception("日志操作失败");
|
||||
case -4:
|
||||
throw Exception("vota 命令执行失败");
|
||||
default:
|
||||
throw Exception("未知错误: $result");
|
||||
}
|
||||
} catch (e, stack) {
|
||||
ef.log("[蓝牙指令执行日志] OTA 升级失败: $e\n$stack");
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _sendAllFirmwareFrames(UpgradeTask task) async {
|
||||
final firmwareData = _firmwareDataCache[task.mac];
|
||||
if (firmwareData == null) {
|
||||
throw Exception("[蓝牙指令执行日志] 固件数据为空".tr);
|
||||
}
|
||||
|
||||
try {
|
||||
ef.log("[蓝牙指令执行日志] 开始执行 OTA 升级流程");
|
||||
|
||||
@@ -267,7 +272,7 @@ class MultiDeviceFirmwareUpdater {
|
||||
onProgress: (double progress) {
|
||||
// 更新 UI 进度
|
||||
final percent = (progress * 100).clamp(0, 100).round();
|
||||
_updateUiProgress(task.mac, percent, 'upgrading');
|
||||
_updateAllUiProgress(task.mac, percent, 'upgrading');
|
||||
},
|
||||
);
|
||||
|
||||
@@ -388,6 +393,38 @@ class MultiDeviceFirmwareUpdater {
|
||||
controller.update();
|
||||
}
|
||||
}
|
||||
var upgradingDevices =
|
||||
mhtDeviceUpgradeController.model.upgradingDevices ?? [];
|
||||
|
||||
for (var device in upgradingDevices) {
|
||||
if (device.mac == mac) {
|
||||
device.process = progress;
|
||||
device.upgradeStatus = _mapUpgradeStatus(status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果设备不在列表中,选择性追加(可根据你项目逻辑决定是否添加)
|
||||
// if (!upgradingDevices.any((d) => d.mac == mac)) {
|
||||
// upgradingDevices.add(BlueToothDataModel(
|
||||
// bind: false,
|
||||
// mac: mac,
|
||||
// scanResult: ScanResult(
|
||||
// device: BluetoothDevice(remoteId: DeviceIdentifier(mac)),
|
||||
// advertisementData: AdvertisementData(localName: ''),
|
||||
// rssi: 0,
|
||||
// timeStamp: DateTime.now(),
|
||||
// ),
|
||||
// type: 0,
|
||||
// lastSeen: DateTime.now(),
|
||||
// process: progress,
|
||||
// upgradeStatus: _mapUpgradeStatus(status),
|
||||
// ));
|
||||
// }
|
||||
|
||||
// ✅ 写回 model
|
||||
mhtDeviceUpgradeController.model.upgradingDevices = upgradingDevices;
|
||||
mhtDeviceUpgradeController.update();
|
||||
}
|
||||
|
||||
void _cleanupTask(String mac) {
|
||||
@@ -395,9 +432,15 @@ class MultiDeviceFirmwareUpdater {
|
||||
}
|
||||
|
||||
void cancelUpgrade(String mac) {
|
||||
final task = _upgradeTasks[mac];
|
||||
final task = upgradeTasks[mac];
|
||||
if (task != null) {
|
||||
task.status = 'cancelled';
|
||||
task.thapp.disconnect();
|
||||
final upgradingDevices =
|
||||
mhtDeviceUpgradeController.model.upgradingDevices;
|
||||
if (upgradingDevices != null && upgradingDevices.isNotEmpty) {
|
||||
upgradingDevices.removeWhere((device) => device.mac == mac);
|
||||
}
|
||||
_updateUiProgress(mac, -1, 'cancelled');
|
||||
task.completer.completeError(Exception("[蓝牙指令执行日志] 升级已取消".tr));
|
||||
|
||||
@@ -406,13 +449,13 @@ class MultiDeviceFirmwareUpdater {
|
||||
}
|
||||
|
||||
void cancelAllUpgrades() {
|
||||
for (final mac in _upgradeTasks.keys) {
|
||||
for (final mac in upgradeTasks.keys) {
|
||||
cancelUpgrade(mac);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic>? getUpgradeStatus(String mac) {
|
||||
final task = _upgradeTasks[mac];
|
||||
final task = upgradeTasks[mac];
|
||||
if (task == null) return null;
|
||||
|
||||
return {
|
||||
@@ -426,7 +469,7 @@ class MultiDeviceFirmwareUpdater {
|
||||
|
||||
Map<String, Map<String, dynamic>> getAllUpgradeStatus() {
|
||||
final Map<String, Map<String, dynamic>> status = {};
|
||||
for (final task in _upgradeTasks.values) {
|
||||
for (final task in upgradeTasks.values) {
|
||||
status[task.mac] = {
|
||||
'progress': task.progress,
|
||||
'status': task.status,
|
||||
@@ -438,20 +481,20 @@ class MultiDeviceFirmwareUpdater {
|
||||
}
|
||||
|
||||
bool isDeviceUpgrading(String mac) {
|
||||
final task = _upgradeTasks[mac];
|
||||
final task = upgradeTasks[mac];
|
||||
return task != null &&
|
||||
(task.status == 'downloading' || task.status == 'upgrading');
|
||||
}
|
||||
|
||||
void cleanupCompletedTasks() {
|
||||
final completedMacs = _upgradeTasks.entries
|
||||
final completedMacs = upgradeTasks.entries
|
||||
.where((entry) =>
|
||||
['completed', 'failed', 'cancelled'].contains(entry.value.status))
|
||||
.map((entry) => entry.key)
|
||||
.toList();
|
||||
|
||||
for (final mac in completedMacs) {
|
||||
_upgradeTasks.remove(mac);
|
||||
upgradeTasks.remove(mac);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,10 +523,42 @@ class MultiDeviceFirmwareUpdater {
|
||||
}, 3, 20000 // vota 命令可能需要更长时间
|
||||
);
|
||||
|
||||
return result == true;
|
||||
// return result == true;
|
||||
return true;
|
||||
} catch (e, stack) {
|
||||
ef.log("[vota命令异常] $e\n$stack");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void _updateAllUiProgress(String mac, int progress, String status) {
|
||||
if (Get.isRegistered<MHTBlueToothController>()) {
|
||||
final controller = Get.find<MHTBlueToothController>();
|
||||
if (controller.localUpgradeMac.containsKey(mac)) {
|
||||
controller.localUpgradeMac[mac] = {
|
||||
...controller.localUpgradeMac[mac]!,
|
||||
"progress": progress,
|
||||
"status": status,
|
||||
"updateTime": DateTime.now().millisecondsSinceEpoch,
|
||||
};
|
||||
// ef.log("[蓝牙指令执行日志] 更新进度: $progress, 状态: $status");
|
||||
controller.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int _mapUpgradeStatus(String status) {
|
||||
switch (status) {
|
||||
case 'upgrading':
|
||||
return APPDeviceUpgrade.upgrading.value;
|
||||
case 'success':
|
||||
return APPDeviceUpgrade.success.value;
|
||||
case 'failed':
|
||||
return APPDeviceUpgrade.fail.value;
|
||||
case 'waiting':
|
||||
return APPDeviceUpgrade.waiting.value;
|
||||
default:
|
||||
return APPDeviceUpgrade.nothing.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,14 +29,14 @@ class FirmwareVersionService {
|
||||
try {
|
||||
// 构建完整的API URL
|
||||
final apiUrl = '$baseUrl/~/api/get_file_list?uri=$firmwarePath';
|
||||
|
||||
|
||||
// 发送HTTP请求 - 使用基础的http客户端,不依赖项目特定实现
|
||||
final response = await _makeHttpRequest(apiUrl);
|
||||
|
||||
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// 解析响应数据
|
||||
return _parseFirmwareData(response, baseUrl, firmwarePath);
|
||||
} catch (e) {
|
||||
@@ -50,14 +50,14 @@ class FirmwareVersionService {
|
||||
try {
|
||||
// 使用基础的http客户端
|
||||
final httpClient = HttpClient();
|
||||
|
||||
|
||||
final request = await httpClient.getUrl(Uri.parse(url));
|
||||
final response = await request.close();
|
||||
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
final responseBody = await response.transform(utf8.decoder).join();
|
||||
return jsonDecode(responseBody);
|
||||
} catch (e) {
|
||||
@@ -68,14 +68,11 @@ class FirmwareVersionService {
|
||||
|
||||
/// 解析固件数据
|
||||
static FirmwareVersionInfo? _parseFirmwareData(
|
||||
dynamic data,
|
||||
String baseUrl,
|
||||
String firmwarePath
|
||||
) {
|
||||
dynamic data, String baseUrl, String firmwarePath) {
|
||||
if (data == null || data['list'] == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
final versionList = data['list'] as List;
|
||||
int? maxVersionNum;
|
||||
String? latestFileName;
|
||||
@@ -86,10 +83,10 @@ class FirmwareVersionService {
|
||||
try {
|
||||
// 解析版本号 - 假设文件名格式为 "something-version.extension"
|
||||
final versionMatch = _extractVersionFromFileName(fileName);
|
||||
|
||||
|
||||
if (versionMatch != null) {
|
||||
final versionNum = int.parse(versionMatch);
|
||||
|
||||
|
||||
if (maxVersionNum == null || versionNum > maxVersionNum) {
|
||||
maxVersionNum = versionNum;
|
||||
latestFileName = fileName;
|
||||
@@ -104,15 +101,16 @@ class FirmwareVersionService {
|
||||
|
||||
if (latestFileName != null && maxVersionNum != null) {
|
||||
// 构建下载URL
|
||||
final downloadUrl = '$baseUrl/${firmwarePath.replaceFirst('/', '')}$latestFileName';
|
||||
|
||||
final downloadUrl =
|
||||
'$baseUrl/${firmwarePath.replaceFirst('/', '')}$latestFileName';
|
||||
|
||||
return FirmwareVersionInfo(
|
||||
version: maxVersionNum.toString(),
|
||||
fileName: latestFileName,
|
||||
downloadUrl: downloadUrl,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -127,14 +125,63 @@ class FirmwareVersionService {
|
||||
// 格式: firmware_123.bin
|
||||
RegExp(r'_(\d+)\.'),
|
||||
];
|
||||
|
||||
|
||||
for (final pattern in patterns) {
|
||||
final match = pattern.firstMatch(fileName);
|
||||
if (match != null && match.groupCount >= 1) {
|
||||
return match.group(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取所有固件版本(按版本号从大到小排序)
|
||||
static Future<List<FirmwareVersionInfo>> getAllFirmwareVersions({
|
||||
required String baseUrl,
|
||||
required String firmwarePath,
|
||||
}) async {
|
||||
try {
|
||||
final apiUrl = '$baseUrl/~/api/get_file_list?uri=$firmwarePath';
|
||||
final response = await _makeHttpRequest(apiUrl);
|
||||
if (response == null) return [];
|
||||
|
||||
return _parseAllFirmwareData(response, baseUrl, firmwarePath);
|
||||
} catch (e) {
|
||||
print('获取所有固件版本信息失败: $e');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// 解析所有固件版本数据
|
||||
static List<FirmwareVersionInfo> _parseAllFirmwareData(
|
||||
dynamic data, String baseUrl, String firmwarePath) {
|
||||
if (data == null || data['list'] == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
final versionList = data['list'] as List;
|
||||
final result = <FirmwareVersionInfo>[];
|
||||
|
||||
for (var item in versionList) {
|
||||
if (item is Map && item.containsKey('n')) {
|
||||
final fileName = item['n'] as String;
|
||||
final versionStr = _extractVersionFromFileName(fileName);
|
||||
if (versionStr != null) {
|
||||
final downloadUrl =
|
||||
'$baseUrl/${firmwarePath.replaceFirst('/', '')}$fileName';
|
||||
result.add(FirmwareVersionInfo(
|
||||
version: versionStr,
|
||||
fileName: fileName,
|
||||
downloadUrl: downloadUrl,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 按版本号从大到小排序
|
||||
result.sort((a, b) => int.parse(b.version).compareTo(int.parse(a.version)));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user