740 lines
26 KiB
Dart
Executable File
740 lines
26 KiB
Dart
Executable File
import 'dart:async';
|
||
import 'dart:convert';
|
||
import 'dart:io';
|
||
import 'dart:typed_data';
|
||
|
||
import 'package:crypto/crypto.dart';
|
||
import 'package:date_format/date_format.dart';
|
||
import 'package:file_picker/file_picker.dart';
|
||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||
import 'package:get/get.dart';
|
||
import 'package:permission_handler/permission_handler.dart';
|
||
import 'package:star_lock/blue/io_protocol/io_getPrivateKey.dart';
|
||
import 'package:star_lock/blue/io_protocol/io_getPublicKey.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_type.dart';
|
||
import 'package:star_lock/main/lockDetail/lockSet/lockTime/getServerDatetime_entity.dart';
|
||
import 'package:star_lock/mine/addLock/nearbyLock/nearbyLock_page.dart';
|
||
import 'package:star_lock/tools/baseGetXController.dart';
|
||
import 'package:star_lock/widget/permission/permission_dialog.dart';
|
||
|
||
import '../../../appRouters.dart';
|
||
import '../../../app_settings/app_settings.dart';
|
||
import '../../../blue/blue_manage.dart';
|
||
import '../../../blue/io_protocol/io_getStarLockStatusInfo.dart';
|
||
import '../../../blue/io_reply.dart';
|
||
import '../../../blue/io_tool/io_tool.dart';
|
||
import '../../../blue/io_tool/manager_event_bus.dart';
|
||
import '../../../blue/sender_manage.dart';
|
||
import '../../../network/api_repository.dart';
|
||
import '../../../tools/storage.dart';
|
||
import 'nearbyLock_state.dart';
|
||
|
||
class NearbyLockLogic extends BaseGetXController {
|
||
final NearbyLockState state = NearbyLockState();
|
||
|
||
int otaCount = 0;
|
||
int otaIndex = 0;
|
||
Uint8List? otaBin;
|
||
int startSecond = 0;
|
||
Map? headJson;
|
||
|
||
String? deviceName;
|
||
StreamSubscription<Reply>? _replySubscription;
|
||
|
||
// 点击连接设备
|
||
void connect(String deviceName) {
|
||
showTitleEasyLoading('${'获取锁信息'.tr} 1/3');
|
||
showBlueConnetctToastTimer(action: () {
|
||
dismissEasyLoading();
|
||
});
|
||
BlueManage().blueSendData(deviceName,
|
||
(BluetoothConnectionState state) async {
|
||
AppLog.log('点击要添加的设备了');
|
||
if (state == BluetoothConnectionState.connected) {
|
||
AppLog.log('开始获取公钥');
|
||
IoSenderManage.getPublicKey(lockId: deviceName);
|
||
} else if (state == BluetoothConnectionState.disconnected) {
|
||
dismissEasyLoading();
|
||
}
|
||
}, isAddEquipment: true);
|
||
}
|
||
|
||
void _initReplySubscription() {
|
||
_replySubscription =
|
||
EventBusManager().eventBus!.on<Reply>().listen((Reply reply) {
|
||
if (reply is GetPublicKeyReply) {
|
||
_replyGetPublicKey(reply);
|
||
}
|
||
|
||
if (reply is GetPrivateKeyReply) {
|
||
_replyGetPrivateKeyKey(reply);
|
||
}
|
||
|
||
// 获取锁状态信息
|
||
if (reply is GetStarLockStatuInfoReply && state.ifCurrentScreen.value) {
|
||
_replyGetStarLockStatusInfo(reply);
|
||
}
|
||
|
||
if (reply is OTAUpgradeReply) {
|
||
if (reply.status == 0x00) {
|
||
//验证通过,开始发送数据包
|
||
dismissEasyLoading();
|
||
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) {
|
||
showToast('固件升级完成'.tr);
|
||
closeOTADAta();
|
||
}
|
||
});
|
||
}
|
||
|
||
Future<void> _replyGetPublicKey(Reply reply) async {
|
||
// 获取公钥
|
||
switch (reply.status) {
|
||
case 0x00:
|
||
//成功
|
||
// AppLog.log('获取公钥成功');
|
||
// 储存公钥
|
||
final List<int> publicKey = reply.data.sublist(3);
|
||
final List<String> saveStrList = changeIntListToStringList(publicKey);
|
||
Storage.setStringList(saveBluePublicKey, saveStrList);
|
||
|
||
// 获取私钥
|
||
// AppLog.log('开始获取私钥');
|
||
showTitleEasyLoading('${'获取锁信息'.tr} 2/3');
|
||
IoSenderManage.getPrivateKey(
|
||
lockId: BlueManage().connectDeviceName,
|
||
keyID: '1',
|
||
authUserID: await Storage.getUid(),
|
||
nowTime: state.serverTime,
|
||
publicKeyData: publicKey,
|
||
needAuthor: 1);
|
||
break;
|
||
default:
|
||
AppLog.log('获取公钥失败');
|
||
break;
|
||
}
|
||
}
|
||
|
||
Future<void> _replyGetPrivateKeyKey(Reply reply) async {
|
||
switch (reply.status) {
|
||
case 0x00:
|
||
// AppLog.log('获取私钥成功');
|
||
//成功
|
||
reply.data.removeAt(0);
|
||
|
||
// 私钥
|
||
final List<int> privateKey = reply.data.sublist(0, 16);
|
||
final List<String> savePrivateKeyList =
|
||
changeIntListToStringList(privateKey);
|
||
await Storage.setStringList(saveBluePrivateKey, savePrivateKeyList);
|
||
|
||
// signKey
|
||
final List<int> signKey = reply.data.sublist(16, 32);
|
||
final List<String> saveSignKeyList = changeIntListToStringList(signKey);
|
||
await Storage.setStringList(saveBlueSignKey, saveSignKeyList);
|
||
|
||
// 时间戳
|
||
final List<int> timestamp = reply.data.sublist(32, 36);
|
||
state.timestampValue = (0xff & timestamp[0]) << 24 |
|
||
(0xff & timestamp[1]) << 16 |
|
||
(0xff & timestamp[2]) << 8 |
|
||
(0xFF & timestamp[3]);
|
||
showTitleEasyLoading('${'获取锁信息'.tr} 3/3');
|
||
_getStarLockStatus();
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 获取星锁状态
|
||
Future<void> _replyGetStarLockStatusInfo(Reply reply) async {
|
||
final int status = reply.data[2];
|
||
dismissEasyLoading();
|
||
switch (status) {
|
||
case 0x00:
|
||
//成功
|
||
// AppLog.log('获取锁状态成功');
|
||
// 厂商名称
|
||
int index = 3;
|
||
final List<int> vendor = reply.data.sublist(index, index + 20);
|
||
final String vendorStr = utf8String(vendor);
|
||
state.lockInfo['vendor'] = vendorStr;
|
||
// state.lockInfo["vendor"] = "XL";
|
||
index = index + 20;
|
||
// AppLog.log('厂商名称 vendorStr:$vendorStr');
|
||
|
||
// 锁设备类型
|
||
final int product = reply.data[index];
|
||
state.lockInfo['product'] = product;
|
||
index = index + 1;
|
||
// AppLog.log('锁设备类型 product:$product');
|
||
|
||
// 产品名称
|
||
final List<int> model = reply.data.sublist(index, index + 20);
|
||
final String modelStr = utf8String(model);
|
||
state.lockInfo['model'] = modelStr;
|
||
// state.lockInfo["model"] = "JL-BLE-01";
|
||
index = index + 20;
|
||
// AppLog.log('产品名称 mmodelStr:$modelStr');
|
||
|
||
// 软件版本
|
||
final List<int> fwVersion = reply.data.sublist(index, index + 20);
|
||
final String fwVersionStr = utf8String(fwVersion);
|
||
state.lockInfo['fwVersion'] = fwVersionStr;
|
||
index = index + 20;
|
||
// AppLog.log('软件版本 fwVersionStr:$fwVersionStr');
|
||
|
||
// 硬件版本
|
||
final List<int> hwVersion = reply.data.sublist(index, index + 20);
|
||
final String hwVersionStr = utf8String(hwVersion);
|
||
state.lockInfo['hwVersion'] = hwVersionStr;
|
||
index = index + 20;
|
||
// AppLog.log('硬件版本 hwVersionStr:$hwVersionStr');
|
||
|
||
// 厂商序列号
|
||
final List<int> serialNum0 = reply.data.sublist(index, index + 16);
|
||
final String serialNum0Str = utf8String(serialNum0);
|
||
state.lockInfo['serialNum0'] = serialNum0Str;
|
||
// state.lockInfo["serialNum0"] = "${DateTime.now().millisecondsSinceEpoch ~/ 10}";
|
||
index = index + 16;
|
||
AppLog.log('厂商序列号 serialNum0Str:$serialNum0Str');
|
||
|
||
// 成品商序列号
|
||
final List<int> serialNum1 = reply.data.sublist(index, index + 16);
|
||
final String serialNum1Str = utf8String(serialNum1);
|
||
state.lockInfo['serialNum1'] = serialNum1Str;
|
||
index = index + 16;
|
||
// AppLog.log('成品商序列号 serialNum1Str:$serialNum1Str');
|
||
|
||
// 蓝牙名称
|
||
final List<int> btDeviceName = reply.data.sublist(index, index + 16);
|
||
final String btDeviceNameStr = utf8String(btDeviceName);
|
||
state.lockInfo['btDeviceName'] = btDeviceNameStr;
|
||
index = index + 16;
|
||
// AppLog.log('蓝牙名称 btDeviceNameStr:$btDeviceNameStr');
|
||
|
||
// 电池剩余电量
|
||
final int battRemCap = reply.data[index];
|
||
state.lockInfo['electricQuantity'] = battRemCap;
|
||
index = index + 1;
|
||
// AppLog.log('电池剩余电量 battRemCap:$battRemCap');
|
||
|
||
// 备用电池剩余电量
|
||
final int battRemCapStandby = reply.data[index];
|
||
state.lockInfo['electricQuantityStandby'] = battRemCapStandby;
|
||
index = index + 1;
|
||
// AppLog.log('电池剩余电量 battRemCap:$battRemCap');
|
||
|
||
// 重置次数
|
||
final List<int> restoreCounter = reply.data.sublist(index, index + 2);
|
||
state.lockInfo['restoreCount'] =
|
||
restoreCounter[0] * 256 + restoreCounter[1];
|
||
index = index + 2;
|
||
// AppLog.log('重置次数 restoreCounter:${restoreCounter[0] * 256 + restoreCounter[1]}');
|
||
|
||
// 重置时间
|
||
final List<int> restoreDate = reply.data.sublist(index, index + 4);
|
||
final int restoreDateValue = (0xff & restoreDate[0]) << 24 |
|
||
(0xff & restoreDate[1]) << 16 |
|
||
(0xff & restoreDate[2]) << 8 |
|
||
(0xFF & restoreDate[3]);
|
||
// String restoreDateStr = DateTool().dateToYMDHNSString(restoreDateValue.toString());
|
||
state.lockInfo['restoreDate'] = restoreDateValue * 1000;
|
||
index = index + 4;
|
||
// AppLog.log('重置时间 restoreDateValue:$restoreDateValue');
|
||
|
||
// 主控芯片型号
|
||
final List<int> icPartNo = reply.data.sublist(index, index + 10);
|
||
final String icPartNoStr = utf8String(icPartNo);
|
||
state.lockInfo['icPartNo'] = icPartNoStr;
|
||
index = index + 10;
|
||
// AppLog.log('主控芯片型号 icPartNoStr:$icPartNoStr');
|
||
|
||
// 有效时间
|
||
final List<int> indate = reply.data.sublist(index, index + 4);
|
||
final int indateValue = (0xff & indate[0]) << 24 |
|
||
(0xff & indate[1]) << 16 |
|
||
(0xff & indate[2]) << 8 |
|
||
(0xFF & indate[3]);
|
||
// String indateStr = DateTool().dateToYMDHNSString("$indateValue");
|
||
state.lockInfo['indate'] = indateValue * 1000;
|
||
index = index + 4;
|
||
// AppLog.log('有效时间 indateValue:$indateValue');
|
||
|
||
// mac地址
|
||
final List<int> macAddress = reply.data.sublist(index, index + 20);
|
||
final String macAddressStr = utf8String(macAddress);
|
||
state.lockInfo['mac'] = macAddressStr;
|
||
index = index + 20;
|
||
// AppLog.log('mac地址 macAddressStr:$macAddressStr');
|
||
|
||
//时区偏移
|
||
state.lockInfo['timezoneOffset'] =
|
||
DateTime.now().timeZoneOffset.inSeconds;
|
||
|
||
// 锁特征值字符串长度
|
||
final int featureValueLength = reply.data[index];
|
||
index = index + 1;
|
||
AppLog.log('锁特征值字符串长度 featureValueLength:$featureValueLength');
|
||
|
||
// 锁特征值说明(本机能支持的功能)
|
||
// 获取到锁给的字符数组
|
||
final int featureNetxLength = index + featureValueLength;
|
||
if (reply.data.length < featureNetxLength) {
|
||
showToast('锁数据异常,请重试'.tr);
|
||
return;
|
||
}
|
||
final List<int> featureValue =
|
||
reply.data.sublist(index, index + featureValueLength);
|
||
final String featureValueStr = asciiString(featureValue);
|
||
state.featureValue = featureValueStr;
|
||
// List allFeatureValueTwoList = charListChangeIntList(featureValue);
|
||
// AppLog.log("featureValueLength:$featureValueLength featureValue:$featureValue \n featureValueStr:$featureValueStr");
|
||
index = index + featureValueLength;
|
||
AppLog.log('锁特征值字符串 featureValueStr:$featureValueStr');
|
||
|
||
// 使能特征值字符串长度
|
||
final int featureEnValLength = reply.data[index];
|
||
index = index + 1;
|
||
AppLog.log('使能特征值字符串长度 featureEnValLength:$featureEnValLength');
|
||
|
||
// 使能锁特征值说明(本机启用的功能)
|
||
final int featureEnNextLength = index + featureEnValLength;
|
||
if (reply.data.length < featureEnNextLength) {
|
||
showToast('锁数据异常,请重试'.tr);
|
||
return;
|
||
}
|
||
final List<int> featureEnVal =
|
||
reply.data.sublist(index, index + featureEnValLength);
|
||
final String featureEnValStr = asciiString(featureEnVal);
|
||
state.featureSettingValue = featureEnValStr;
|
||
// List allFeatureEnValTwoList = charListChangeIntList(featureEnVal);
|
||
// AppLog.log("featureEnValLength:$featureEnValLength featureEnVal:$featureEnVal \n featureEnValStr:$featureEnValStr");
|
||
index = index + featureEnValLength;
|
||
AppLog.log('使能锁特征值说明 featureEnValStr:$featureEnValStr');
|
||
|
||
// 支持的带参数特征值的总条目数
|
||
// var featureParaTotal = reply.data[index];
|
||
|
||
final List<int> featureParaTotalList = reply.data.sublist(index);
|
||
state.featureSettingParams = featureParaTotalList;
|
||
AppLog.log('featureParaTotalList:$featureParaTotalList');
|
||
|
||
// Get.toNamed(Routers.lockAddressGaoDePage, arguments: <String, Object>{
|
||
// 'pwdTimestamp': state.timestampValue * 1000,
|
||
// 'lockInfo': state.lockInfo,
|
||
// 'featureValue': state.featureValue,
|
||
// 'featureSettingValue': state.featureSettingValue,
|
||
// 'featureSettingParams': state.featureSettingParams,
|
||
// });
|
||
Get.toNamed(Routers.saveLockPage, arguments: <String, Object?>{
|
||
'addressInfo': {},
|
||
'pwdTimestamp': state.timestampValue * 1000,
|
||
'lockInfo': state.lockInfo,
|
||
'featureValue': state.featureValue,
|
||
'featureSettingValue': state.featureSettingValue,
|
||
'featureSettingParams': state.featureSettingParams,
|
||
'isFromMap': 0,
|
||
});
|
||
|
||
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: state.serverTime,
|
||
unixTimeStamp: getLocalTime(),
|
||
isBeforeAddUser: true,
|
||
privateKey: getPrivateKeyList,
|
||
);
|
||
break;
|
||
default:
|
||
//失败
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 获取锁状态
|
||
Future<void> _getStarLockStatus() async {
|
||
// 进来之后首先连接
|
||
// BlueManage().bludSendData(BlueManage().connectDeviceName, (BluetoothConnectionState state) async {
|
||
// if (state == BluetoothConnectionState.connected) {
|
||
// dismissEasyLoading();
|
||
|
||
// AppLog.log('开始获取锁状态');
|
||
final List<String>? privateKey =
|
||
await Storage.getStringList(saveBluePrivateKey);
|
||
final List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
|
||
|
||
final String getUTCDate = formatDate(
|
||
DateTime.fromMillisecondsSinceEpoch(state.serverTime * 1000),
|
||
<String>[yyyy, '-', mm, '-', dd, ' ', HH, ':', nn, ':', ss]);
|
||
final String getLocalDate = formatDate(
|
||
DateTime.fromMillisecondsSinceEpoch(getLocalTime() * 1000),
|
||
<String>[yyyy, '-', mm, '-', dd, ' ', HH, ':', nn, ':', ss]);
|
||
|
||
// AppLog.log('state.serverTime:${state.serverTime} getUTCDate:$getUTCDate '
|
||
// 'getLocalTime:${getLocalTime()} getLocalDate:$getLocalDate '
|
||
// '差值:${getLocalTime() - state.serverTime}');
|
||
IoSenderManage.senderGetStarLockStatuInfo(
|
||
lockID: BlueManage().connectDeviceName,
|
||
userID: await Storage.getUid(),
|
||
utcTimeStamp: state.serverTime,
|
||
unixTimeStamp: getLocalTime(),
|
||
isBeforeAddUser: true,
|
||
privateKey: getPrivateKeyList,
|
||
);
|
||
}
|
||
|
||
void startScanBlueList() {
|
||
BlueManage().startScan(2000, DeviceType.blue, (List<ScanResult> list) {
|
||
state.devices.clear();
|
||
|
||
for (final device in list) {
|
||
final String? serviceUuid =
|
||
device.advertisementData.serviceUuids.isNotEmpty
|
||
? device.advertisementData.serviceUuids[0].toString()
|
||
: null;
|
||
|
||
if (serviceUuid != null && !isPaired(serviceUuid)) {
|
||
state.devices.add(device);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/// 判断是否已配对(支持 128-bit 和 32-bit)
|
||
bool isPaired(String serviceUuid) {
|
||
if (serviceUuid.length < 6) return false; // 最短需要 6 个字符才能判断
|
||
|
||
try {
|
||
if (serviceUuid.length >= 32) {
|
||
// 128-bit UUID:检查第 31 和 32 位
|
||
String status = serviceUuid.substring(30, 32);
|
||
return status == '01'; // '01' 表示已配对
|
||
} else if (serviceUuid.length >= 5) {
|
||
// 32-bit UUID:检查第 4 和 5 位
|
||
String status = serviceUuid.substring(4, 6);
|
||
return status == '01'; // '01' 表示已配对
|
||
}
|
||
return false; // 如果长度不足,则返回 false
|
||
} catch (e) {
|
||
return false; // 如果索引越界或其他错误,返回 false
|
||
}
|
||
}
|
||
|
||
/// 判断是否休眠(支持 128-bit 和 32-bit)
|
||
bool isSleeping(String serviceUuid) {
|
||
if (serviceUuid.length < 8) return false; // 最短需要 8 个字符才能判断
|
||
|
||
try {
|
||
if (serviceUuid.length >= 34) {
|
||
// 128-bit UUID:检查第 33 和 34 位
|
||
String status = serviceUuid.substring(32, 34);
|
||
return status == '00'; // '00' 表示休眠
|
||
} else if (serviceUuid.length >= 7) {
|
||
// 32-bit UUID:检查第 6 和 7 位
|
||
String status = serviceUuid.substring(6, 8);
|
||
return status == '00'; // '00' 表示休眠
|
||
}
|
||
return false; // 如果长度不足,则返回 false
|
||
} catch (e) {
|
||
return false; // 如果索引越界或其他错误,返回 false
|
||
}
|
||
}
|
||
|
||
void stopScanBlueList() {
|
||
BlueManage().disconnect();
|
||
BlueManage().stopScan();
|
||
}
|
||
|
||
// 点击连接设备,升级 ota 固件
|
||
void oTAUpgrade(String deviceName) {
|
||
showTitleEasyLoading('连接设备中...'.tr);
|
||
this.deviceName = deviceName;
|
||
BlueManage().blueSendData(deviceName,
|
||
(BluetoothConnectionState state) async {
|
||
// AppLog.log('连接设备');
|
||
if (state == BluetoothConnectionState.connected) {
|
||
// AppLog.log('连接成功');
|
||
dismissEasyLoading();
|
||
otaUpdate();
|
||
} else if (state == BluetoothConnectionState.disconnected) {
|
||
// AppLog.log('连接失败');
|
||
dismissEasyLoading();
|
||
}
|
||
}, isAddEquipment: true);
|
||
}
|
||
|
||
//手动升级
|
||
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();
|
||
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;
|
||
blueOTAUpgrade(headJson!, <int>[0, 0, 0, 0]);
|
||
}
|
||
|
||
//蓝牙操作 ota 升级
|
||
void blueOTAUpgrade(Map data, List<int> token) {
|
||
if (deviceName == null) {
|
||
AppLog.log('blueOTAUpgrade:设备名字为 null');
|
||
return;
|
||
}
|
||
BlueManage().blueSendData(deviceName!,
|
||
(BluetoothConnectionState deviceConnectionState) async {
|
||
if (deviceConnectionState == BluetoothConnectionState.connected) {
|
||
final String uid = await Storage.getUid() ?? '';
|
||
BlueManage().writeCharacteristicWithResponse(OTAUpgradeCommand(
|
||
lockID: deviceName,
|
||
userID: uid,
|
||
keyID: deviceName,
|
||
platform: int.tryParse(data['platform']) ?? 0,
|
||
product: int.tryParse(data['product']) ?? 0,
|
||
hwVersion: data['hwVersion'],
|
||
fwVersion: data['fwVersion'],
|
||
fwSize: data['fwSize'],
|
||
fwMD5: data['fwMd5'],
|
||
needAuthor: 1,
|
||
token: token,
|
||
encrypt: false,
|
||
).packageData());
|
||
} else if (deviceConnectionState ==
|
||
BluetoothConnectionState.disconnected) {}
|
||
}, isAddEquipment: true);
|
||
}
|
||
|
||
//循环传输升级固件包
|
||
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;
|
||
}
|
||
if (otaCount <= otaIndex) {
|
||
final int now = DateTime.now().millisecondsSinceEpoch;
|
||
// final String msg = '传输完成 时间:${now - startSecond}秒 otaCount:$otaCount otaIndex:$otaIndex ';
|
||
closeOTADAta();
|
||
AppLog.log(
|
||
'传输完成 时间:${now - startSecond}秒 otaCount:$otaCount otaIndex:$otaIndex ');
|
||
// 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;
|
||
state.oTAProgressDialog = true;
|
||
Get.dialog(
|
||
OTAProgressDialog(
|
||
logic: this,
|
||
),
|
||
barrierDismissible: false)
|
||
.then((dynamic value) => state.oTAProgressDialog = false);
|
||
}
|
||
|
||
//清楚 ata 安装文件
|
||
void closeOTADAta() {
|
||
if (state.oTAProgressDialog) {
|
||
Get.back();
|
||
}
|
||
state.otaUpdateIng.value = false;
|
||
state.otaProgress.value = 0;
|
||
otaIndex = 0;
|
||
otaCount = 0;
|
||
startSecond = 0;
|
||
otaBin = null;
|
||
}
|
||
|
||
// 拦截返回事件
|
||
void getBack() {
|
||
if (state.otaUpdateIng.value) {
|
||
closeOTADAta();
|
||
} else {
|
||
Get.back();
|
||
}
|
||
}
|
||
|
||
// 检查文件头
|
||
Future<Map?> getHeadFile(Uint8List data) async {
|
||
if (data.length <= 16) {
|
||
showToast('错误固件,请选择正确的文件'.tr);
|
||
return null;
|
||
}
|
||
// 检查文件头
|
||
String header;
|
||
try {
|
||
header = utf8.decode(data.sublist(0, 12));
|
||
} 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('---> $md5Str ${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> getServerDatetime() async {
|
||
final GetServerDatetimeEntity entity =
|
||
await ApiRepository.to.getServerDatetimeData(isUnShowLoading: false);
|
||
if (entity.errorCode!.codeIsSuccessful) {
|
||
state.serverTime = entity.data!.date! ~/ 1000;
|
||
|
||
if (state.otaState.value) {
|
||
oTAUpgrade(state.selectLockName.value);
|
||
} else {
|
||
connect(state.selectLockName.value);
|
||
}
|
||
}
|
||
}
|
||
|
||
int getLocalTime() {
|
||
final DateTime now = DateTime.now();
|
||
final Duration timeZoneOffset = now.timeZoneOffset;
|
||
AppLog.log('timeZoneOffset.inSeconds:$timeZoneOffset.inSeconds');
|
||
return state.serverTime + timeZoneOffset.inSeconds;
|
||
}
|
||
|
||
@override
|
||
void onReady() {
|
||
super.onReady();
|
||
getNearByLimits();
|
||
}
|
||
|
||
@override
|
||
void onInit() {
|
||
super.onInit();
|
||
}
|
||
|
||
@override
|
||
void onClose() {
|
||
_replySubscription?.cancel();
|
||
super.onClose();
|
||
}
|
||
|
||
Future<void> getNearByLimits() async {
|
||
if (!Platform.isIOS) {
|
||
// bool bluetoothRequest = false;
|
||
// try {
|
||
// bluetoothRequest = await PermissionDialog.requestBluetooth();
|
||
// AppLog.log('bluetoothRequest:$bluetoothRequest');
|
||
// if (!bluetoothRequest) {
|
||
// return;
|
||
// }
|
||
// } catch (e) {
|
||
// AppLog.log('bluetoothRequest:$e');
|
||
// }
|
||
final bool bluetoothRequest = await PermissionDialog.requestBluetooth();
|
||
final bool locationRequest =
|
||
await PermissionDialog.request(Permission.location);
|
||
AppLog.log('locationRequest:$locationRequest');
|
||
if (!bluetoothRequest || !locationRequest) {
|
||
return;
|
||
}
|
||
}
|
||
_initReplySubscription();
|
||
state.ifCurrentScreen.value = true;
|
||
startScanBlueList();
|
||
}
|
||
}
|