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? _replySubscription; int otaCount = 0; int otaIndex = 0; Uint8List? otaBin; int startSecond = 0; Map? headJson; FwVersionEntity? entity; String model = ''; String currentVersion = ''; //手动升级 Future 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 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!, [0, 0, 0, 0]); EasyLoading.show( status: '设备连接中...'.tr, maskType: EasyLoadingMaskType.black); Future.delayed(const Duration(seconds: 4), EasyLoading.dismiss); }); } //蓝牙操作 ota 升级 void blueOTAUpgrade(Map data, List token) { BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { if (deviceConnectionState == BluetoothConnectionState.connected) { final List? privateKey = await Storage.getStringList(saveBluePrivateKey); final List getPrivateKeyList = changeStringListToIntList(privateKey!); final List? signKey = await Storage.getStringList(saveBlueSignKey); final List 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 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 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 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 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 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 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 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? privateKey = await Storage.getStringList(saveBluePrivateKey); final List 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 _replyGetStarLockStatusInfo(Reply reply) async { final int status = reply.data[2]; state.loading.value = false; switch (status) { case 0x00: //成功 dismissEasyLoading(); cancelBlueConnetctToastTimer(); // 设备型号 final List modelList = reply.data.sublist(24, 43); // 设备型号 final List fwVersion = reply.data.sublist(44, 63); model = utf8String(modelList); currentVersion = utf8String(fwVersion); checkUpData(); break; case 0x06: //无权限 final List? privateKey = await Storage.getStringList(saveBluePrivateKey); final List 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().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(); } }