371 lines
12 KiB
Dart
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:star_lock/blue/blue_manage.dart';
import 'package:star_lock/blue/io_protocol/io_getStarLockStatusInfo.dart';
import 'package:star_lock/blue/io_protocol/io_otaUpgrade.dart';
import 'package:star_lock/blue/io_protocol/io_processOtaUpgrade.dart';
import 'package:star_lock/blue/io_reply.dart';
import 'package:star_lock/blue/io_tool/io_tool.dart';
import 'package:star_lock/blue/io_tool/manager_event_bus.dart';
import 'package:star_lock/blue/sender_manage.dart';
import 'package:star_lock/main/lockDetail/lockSet/lockEscalation/version_entity.dart';
import 'package:star_lock/network/api_repository.dart';
import 'package:star_lock/tools/baseGetXController.dart';
import 'package:star_lock/tools/commonDataManage.dart';
import 'package:star_lock/tools/showTipView.dart';
import 'package:star_lock/tools/storage.dart';
import 'package:star_lock/widget/permission/permission_dialog.dart';
import '../../../../app_settings/app_settings.dart';
import 'lockEscalation_state.dart';
class LockEscalationLogic extends BaseGetXController {
LockEscalationState state = LockEscalationState();
StreamSubscription<Reply>? _replySubscription;
int otaCount = 0;
int otaIndex = 0;
Uint8List? otaBin;
int startSecond = 0;
Map? headJson;
FwVersionEntity? entity;
String model = '';
String currentVersion = '';
//手动升级
Future<void> otaUpdate() async {
final bool status = await PermissionDialog.requestStorage();
if (status != true) {
return;
}
final FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result == null || result.files.single.path is! String) {
return;
}
final File file = File(result.files.single.path!);
final Uint8List data = await file.readAsBytes();
stateUpData(data);
}
Future<void> stateUpData(Uint8List data) async {
headJson = await getHeadFile(data);
if (headJson is! Map) {
return;
}
otaBin = await checkFile(data, headJson!);
if (otaBin == null) {
return;
}
final String md5Str = md5.convert(otaBin!).toString();
headJson!['fwMd5'] = md5Str;
ShowTipView().showIosTipWithContentDialog('未避免异常情况,请在门打开时升级'.tr, () async {
blueOTAUpgrade(headJson!, <int>[0, 0, 0, 0]);
EasyLoading.show(
status: '设备连接中...'.tr, maskType: EasyLoadingMaskType.black);
Future<void>.delayed(const Duration(seconds: 4), EasyLoading.dismiss);
});
}
//蓝牙操作 ota 升级
void blueOTAUpgrade(Map<dynamic, dynamic> data, List<int> token) {
BlueManage().blueSendData(BlueManage().connectDeviceName,
(BluetoothConnectionState deviceConnectionState) async {
if (deviceConnectionState == BluetoothConnectionState.connected) {
final List<String>? privateKey =
await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList =
changeStringListToIntList(privateKey!);
final List<String>? signKey =
await Storage.getStringList(saveBlueSignKey);
final List<int> signKeyDataList = changeStringListToIntList(signKey!);
final String uid = await Storage.getUid() ?? '';
BlueManage().writeCharacteristicWithResponse(OTAUpgradeCommand(
lockID: BlueManage().connectDeviceName,
userID: uid,
keyID: BlueManage().connectDeviceName,
platform: int.tryParse(data['platform'] ?? '0') ?? 0,
product: int.tryParse(data['product'] ?? '0') ?? 0,
hwVersion: data['hwVersion'],
fwVersion: data['fwVersion'],
fwSize: data['fwSize'],
fwMD5: data['fwMd5'],
needAuthor: 1,
token: token,
signKey: signKeyDataList,
privateKey: getPrivateKeyList)
.packageData());
} else if (deviceConnectionState ==
BluetoothConnectionState.disconnected) {}
});
}
//循环传输升级固件包
Future<void> processOtaUpgrade() async {
if (!state.otaUpdateIng.value) {
return;
}
final int length = otaBin?.length ?? 0;
if (otaCount == 0) {
//首次
final int difference = length % 240;
otaCount = length ~/ 240 + (difference > 0 ? 1 : 0);
startSecond = DateTime.now().millisecondsSinceEpoch ~/ 1000;
startOTAData();
}
if (otaCount <= otaIndex) {
final int now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
final String msg =
'传输完成 时间:${now - startSecond}秒 otaCount:$otaCount otaIndex:$otaIndex ';
closeOTADAta();
AppLog.log(msg);
// showToast(msg);
return;
}
final int star = otaIndex * 240;
int end = (otaIndex + 1) * 240;
if (end > length) {
end = length;
}
final int size = end - star;
final List<int> data = otaBin!.sublist(star, end);
state.otaProgress.value = otaIndex / otaCount;
await BlueManage().writeCharacteristicWithResponse(
ProcessOtaUpgradeCommand(index: otaIndex, size: size, data: data)
.packageData());
}
//开始 ota升级
void startOTAData() {
state.otaUpdateIng.value = true;
}
//清楚 ata 安装文件
void closeOTADAta() {
state.otaUpdateIng.value = false;
state.otaProgress.value = 0;
otaIndex = 0;
otaCount = 0;
startSecond = 0;
otaBin = null;
}
// 拦截返回事件
void getBack() {
if (state.otaUpdateIng.value) {
ShowTipView().showIosTipWithContentDialog('升级中,是否退出'.tr, () {
closeOTADAta();
Get.back();
});
} else {
Get.back();
}
}
// 检查文件头
Future<Map?> getHeadFile(Uint8List data) async {
if (data.length <= 16) {
showToast('错误固件,请选择正确的文件'.tr);
return null;
}
// 检查文件头
String header;
try {
final Uint8List list = data.sublist(0, 12);
header = utf8.decode(list);
} catch (e) {
showToast('非SYD固件请选择正确的文件'.tr);
return null;
}
if (header != 'SYD-BIN-DATA') {
showToast('非SYD固件请选择正确的文件'.tr);
return null;
}
// 解析元数据长度
Uint8List metaLenList;
int metaLen;
try {
metaLenList = data.sublist(12, 16);
metaLen = ByteData.sublistView(metaLenList).getUint32(0);
} catch (e) {
showToast('文件校验失败 0x01'.tr);
return null;
}
if (metaLen < 2 || metaLen > 10240) {
showToast('文件校验失败 0x01'.tr);
return null;
}
// 读取和解析元数据
Uint8List metaStrList;
String metaStr;
try {
metaStrList = data.sublist(16, 16 + metaLen);
metaStr = utf8.decode(metaStrList);
} catch (e) {
showToast('解析元数据失败,请选择正确的文件'.tr);
return null;
}
AppLog.log(metaStr);
final meta = jsonDecode(metaStr);
if (meta is! Map) {
showToast('解析元数据失败,请选择正确的文件'.tr);
return null;
}
return meta..['metaLen'] = metaLen;
}
//检测升级文件并读取 bin
Future<Uint8List?> checkFile(Uint8List data, Map meta) async {
final num binOffset = 16 + (meta['metaLen'] ?? 0);
// 获取固件数据部分
final Uint8List bin = data.sublist(binOffset.toInt(), data.length);
//md5 校验有问题,暂时不解析
final String md5Str = md5.convert(bin).toString().toUpperCase();
AppLog.log('固件 md5 检验md5$md5Str 固件信息 md5${meta['fwMd5']}');
if (md5Str != meta['fwMd5']) {
showToast('文件校验失败 0x02'.tr);
return null;
}
if (bin.length != meta['fwSize']) {
showToast('文件校验失败 0x03'.tr);
return null;
}
return bin;
}
//检查最新版本
Future<void> checkUpData() async {
state.showVersion.value = currentVersion;
entity = await ApiRepository.to
.getFwVersion(model: model, currentVersion: currentVersion);
state.isShowUpDataBtn.value = entity?.data?.isUpdate == 1;
state.showNewVersion.value = entity?.data?.version ?? '';
}
//下载升级
Future<void> downloadTheFile() async {
if (entity?.data?.downloadUrl == null) {
return;
}
final String url = entity!.data!.downloadUrl!;
final http.Response response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
stateUpData(response.bodyBytes);
}
}
// 获取锁状态
Future<void> getStarLockStatus() async {
state.loading.value = true;
showBlueConnetctToastTimer(action: () {
state.loading.value = false;
});
BlueManage().blueSendData(BlueManage().connectDeviceName,
(BluetoothConnectionState deviceConnectionState) async {
if (deviceConnectionState == BluetoothConnectionState.connected) {
final List<String>? privateKey =
await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList =
changeStringListToIntList(privateKey!);
IoSenderManage.senderGetStarLockStatuInfo(
lockID: BlueManage().connectDeviceName,
userID: await Storage.getUid(),
utcTimeStamp: 0,
unixTimeStamp: 0,
isBeforeAddUser: false,
privateKey: getPrivateKeyList);
} else if (deviceConnectionState ==
BluetoothConnectionState.disconnected) {
cancelBlueConnetctToastTimer();
state.loading.value = false;
}
});
}
// 获取星锁状态
Future<void> _replyGetStarLockStatusInfo(Reply reply) async {
final int status = reply.data[2];
state.loading.value = false;
switch (status) {
case 0x00:
//成功
dismissEasyLoading();
cancelBlueConnetctToastTimer();
// 设备型号
final List<int> modelList = reply.data.sublist(24, 43);
// 设备型号
final List<int> fwVersion = reply.data.sublist(44, 63);
model = utf8String(modelList);
currentVersion = utf8String(fwVersion);
checkUpData();
break;
case 0x06:
//无权限
final List<String>? privateKey =
await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList =
changeStringListToIntList(privateKey!);
IoSenderManage.senderGetStarLockStatuInfo(
lockID: BlueManage().connectDeviceName,
userID: await Storage.getUid(),
utcTimeStamp: 0,
unixTimeStamp: 0,
isBeforeAddUser: false,
privateKey: getPrivateKeyList,
);
break;
default:
//失败
break;
}
}
@override
void onReady() {
super.onReady();
}
@override
void onInit() {
super.onInit();
_replySubscription =
EventBusManager().eventBus!.on<Reply>().listen((Reply reply) {
if (reply is OTAUpgradeReply) {
if (reply.status == 0x00) {
//验证通过,开始发送数据包
startOTAData();
processOtaUpgrade();
} else if (reply.status == 0x06) {
blueOTAUpgrade(headJson!, reply.token);
}
} else if (reply is ProcessOtaUpgradeReply && reply.status == 0x00) {
otaIndex++;
processOtaUpgrade();
} else if (reply is ConfirmationOTAUpgradeReply && reply.status == 0x00) {
ApiRepository.to.getLockUpdateLockInfo(
lockId: CommonDataManage().currentKeyInfo.lockId ?? 0,
fwVersion: state.showNewVersion.value);
closeOTADAta();
showToast('固件升级完成'.tr);
} else if (reply is GetStarLockStatuInfoReply) {
_replyGetStarLockStatusInfo(reply);
}
});
getStarLockStatus();
}
@override
void onClose() {
_replySubscription?.cancel();
}
}