2024-05-07 16:45:59 +08:00

632 lines
21 KiB
Dart
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: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/mine/addLock/nearbyLock/nearbyLock_page.dart';
import 'package:star_lock/permission/permission_dialog.dart';
import 'package:star_lock/tools/baseGetXController.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 '../../../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("获取锁信息 1/3");
// if(state.sureBtnState.value == 1){
// return;
// }
// state.sureBtnState.value = 1;
// showEasyLoading();
showBlueConnetctToastTimer(action: () {
dismissEasyLoading();
// state.sureBtnState.value = 0;
});
BlueManage().bludSendData(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) {
if (reply is GetPublicKeyReply) {
_replyGetPublicKey(reply);
}
if (reply is GetPrivateKeyReply) {
_replyGetPrivateKeyKey(reply);
}
// 获取锁状态信息
if (reply is GetStarLockStatuInfoReply) {
_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 {
// dismissEasyLoading();
// 获取公钥
switch (reply.status) {
case 0x00:
//成功
AppLog.log("获取公钥成功");
// 储存公钥
var publicKey = reply.data.sublist(3);
var saveStrList = changeIntListToStringList(publicKey);
Storage.setStringList(saveBluePublicKey, saveStrList);
// 获取私钥
AppLog.log("开始获取私钥");
showTitleEasyLoading("获取锁信息 2/3");
IoSenderManage.getPrivateKey(
lockId: BlueManage().connectDeviceName,
keyID: "1",
authUserID: await Storage.getUid(),
nowTime: DateTime.now().millisecondsSinceEpoch ~/ 1000,
publicKeyData: publicKey,
needAuthor: 1);
break;
default:
// state.sureBtnState.value = 0;
AppLog.log("获取公钥失败");
break;
}
}
Future<void> _replyGetPrivateKeyKey(Reply reply) async {
switch (reply.status) {
case 0x00:
AppLog.log("获取私钥成功");
//成功
reply.data.removeAt(0);
// 私钥
List<int> privateKey = reply.data.sublist(0, 16);
var savePrivateKeyList = changeIntListToStringList(privateKey);
Storage.setStringList(saveBluePrivateKey, savePrivateKeyList);
// signKey
List<int> signKey = reply.data.sublist(16, 32);
var saveSignKeyList = changeIntListToStringList(signKey);
Storage.setStringList(saveBlueSignKey, saveSignKeyList);
// 时间戳
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("获取锁信息 3/3");
_getStarLockStatus();
break;
default:
// state.sureBtnState.value = 0;
break;
}
}
// 获取星锁状态
Future<void> _replyGetStarLockStatusInfo(Reply reply) async {
int status = reply.data[2];
switch (status) {
case 0x00:
//成功
AppLog.log("获取锁状态成功");
// 厂商名称
var vendor = reply.data.sublist(3, 23);
var vendorStr = utf8String(vendor);
state.lockInfo["vendor"] = vendorStr;
// state.lockInfo["vendor"] = "XL";
AppLog.log("厂商名称 vendorStr:$vendorStr");
// 锁设备类型
var product = reply.data[23];
state.lockInfo["product"] = product;
AppLog.log("锁设备类型 product:$product");
// 产品名称
var model = reply.data.sublist(24, 44);
var modelStr = utf8String(model);
state.lockInfo["model"] = modelStr;
// state.lockInfo["model"] = "JL-BLE-01";
AppLog.log("产品名称 mmodelStr:$modelStr");
// 软件版本
var fwVersion = reply.data.sublist(44, 64);
var fwVersionStr = utf8String(fwVersion);
state.lockInfo["fwVersion"] = fwVersionStr;
AppLog.log("软件版本 fwVersionStr:$fwVersionStr");
// 硬件版本
var hwVersion = reply.data.sublist(64, 84);
var hwVersionStr = utf8String(hwVersion);
state.lockInfo["hwVersion"] = hwVersionStr;
AppLog.log("硬件版本 hwVersionStr:$hwVersionStr");
// 厂商序列号
var serialNum0 = reply.data.sublist(84, 100);
var serialNum0Str = utf8String(serialNum0);
state.lockInfo["serialNum0"] = serialNum0Str;
// state.lockInfo["serialNum0"] = "${DateTime.now().millisecondsSinceEpoch ~/ 10}";
AppLog.log("厂商序列号 serialNum0Str:${serialNum0Str.length}");
// 成品商序列号
var serialNum1 = reply.data.sublist(100, 116);
var serialNum1Str = utf8String(serialNum1);
state.lockInfo["serialNum1"] = serialNum1Str;
AppLog.log("成品商序列号 serialNum1Str:$serialNum1Str");
// 蓝牙名称
var btDeviceName = reply.data.sublist(116, 132);
var btDeviceNameStr = utf8String(btDeviceName);
state.lockInfo["btDeviceName"] = btDeviceNameStr;
AppLog.log("蓝牙名称 btDeviceNameStr:$btDeviceNameStr");
// 电池剩余电量
var battRemCap = reply.data[132];
state.lockInfo["electricQuantity"] = battRemCap;
AppLog.log("电池剩余电量 battRemCap:$battRemCap");
// 备用电池剩余电量
// var battRemCapStandby = reply.data[133];
// state.lockInfo["electricQuantityStandby"] = battRemCapStandby;
// AppLog.log("电池剩余电量 battRemCap:$battRemCap");
// 重置次数
var restoreCounter = reply.data.sublist(133, 135);
state.lockInfo["restoreCount"] =
restoreCounter[0] * 256 + restoreCounter[1];
AppLog.log(
"重置次数 restoreCounter:${restoreCounter[0] * 256 + restoreCounter[1]}");
// 重置时间
var restoreDate = reply.data.sublist(135, 139);
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;
AppLog.log("重置时间 restoreDateValue:$restoreDateValue");
// 主控芯片型号
var icPartNo = reply.data.sublist(139, 149);
var icPartNoStr = utf8String(icPartNo);
state.lockInfo["icPartNo"] = icPartNoStr;
AppLog.log("主控芯片型号 icPartNoStr:$icPartNoStr");
// 有效时间
var indate = reply.data.sublist(149, 153);
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;
AppLog.log("有效时间 indateValue:$indateValue");
// mac地址
var macAddress = reply.data.sublist(153, 173);
var macAddressStr = utf8String(macAddress);
state.lockInfo["mac"] = macAddressStr;
AppLog.log("mac地址 macAddressStr:$macAddressStr");
var index = 173;
// 锁特征值字符串长度
var featureValueLength = reply.data[index];
AppLog.log("锁特征值字符串长度 featureValueLength:$featureValueLength");
// 锁特征值说明(本机能支持的功能)
// 获取到锁给的字符数组
var featureNetxLength = index + featureValueLength + 1;
if (reply.data.length < featureNetxLength) {
showToast("锁数据异常,请重试");
return;
}
var featureValue =
reply.data.sublist(index + 1, index + featureValueLength + 1);
String featureValueStr = asciiString(featureValue);
state.featureValue = featureValueStr;
// List allFeatureValueTwoList = charListChangeIntList(featureValue);
// AppLog.log("featureValueLength:$featureValueLength featureValue:$featureValue \n featureValueStr:$featureValueStr");
index = index + featureValueLength + 1;
AppLog.log("锁特征值字符串 featureValueStr:$featureValueStr");
// 使能特征值字符串长度
var featureEnValLength = reply.data[index];
AppLog.log("使能特征值字符串长度 featureEnValLength:$featureEnValLength");
// 使能锁特征值说明(本机启用的功能)
var featureEnNextLength = index + featureEnValLength + 1;
if (reply.data.length < featureEnNextLength) {
showToast("锁数据异常,请重试");
return;
}
var featureEnVal =
reply.data.sublist(index + 1, index + featureEnValLength + 1);
String featureEnValStr = asciiString(featureEnVal);
state.featureSettingValue = featureEnValStr;
// List allFeatureEnValTwoList = charListChangeIntList(featureEnVal);
// AppLog.log("featureEnValLength:$featureEnValLength featureEnVal:$featureEnVal \n featureEnValStr:$featureEnValStr");
index = index + featureEnValLength + 1;
AppLog.log("使能锁特征值说明 featureEnValStr:$featureEnValStr");
// 支持的带参数特征值的总条目数
// var featureParaTotal = reply.data[index];
var featureParaTotalList = reply.data.sublist(index);
state.featureSettingParams = featureParaTotalList;
AppLog.log("featureParaTotalList:$featureParaTotalList");
Get.toNamed(Routers.lockAddressGaoDePage, arguments: {
"pwdTimestamp": state.timestampValue * 1000,
"lockInfo": state.lockInfo,
"featureValue": state.featureValue,
"featureSettingValue": state.featureSettingValue,
"featureSettingParams": state.featureSettingParams,
});
break;
case 0x06:
//无权限
var privateKey = await Storage.getStringList(saveBluePrivateKey);
List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
// IoSenderManage.senderGetLockStatu(
// lockID:BlueManage().connectDeviceName,
// userID:await Storage.getUid(),
// privateKey:getPrivateKeyList,
// );
IoSenderManage.senderGetStarLockStatuInfo(
lockID: BlueManage().connectDeviceName,
userID: await Storage.getUid(),
isBeforeAddUser: true,
privateKey: getPrivateKeyList,
);
break;
default:
//失败
// state.sureBtnState.value = 0;
break;
}
}
// 获取锁状态
Future<void> _getStarLockStatus() async {
// 进来之后首先连接
// BlueManage().bludSendData(BlueManage().connectDeviceName, (BluetoothConnectionState state) async {
// if (state == BluetoothConnectionState.connected) {
// dismissEasyLoading();
AppLog.log("开始获取锁状态");
var privateKey = await Storage.getStringList(saveBluePrivateKey);
List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
// IoSenderManage.senderGetLockStatu(
// lockID:BlueManage().connectDeviceName,
// userID:await Storage.getUid(),
// privateKey:getPrivateKeyList,
// );
IoSenderManage.senderGetStarLockStatuInfo(
lockID: BlueManage().connectDeviceName,
userID: await Storage.getUid(),
isBeforeAddUser: true,
privateKey: getPrivateKeyList,
);
// } else if (state == BluetoothConnectionState.disconnected) {
// dismissEasyLoading();
// }
// }, isAddEquipment: true);
}
void startScanBlueList() {
BlueManage().startScan(2000, (List<ScanResult> list) {
state.devices.clear();
for (int i = 0; i < list.length; i++) {
ScanResult device = list[i];
if (((device.advertisementData.serviceUuids.isNotEmpty
? device.advertisementData.serviceUuids[0]
: "")
.toString()[31] !=
"1")) {
state.devices.add(list[i]);
}
}
});
}
void stopScanBlueList() {
BlueManage().disconnect();
BlueManage().stopScan();
}
// 点击连接设备,升级 ota 固件
void oTAUpgrade(String deviceName) {
showTitleEasyLoading("连接设备中...");
this.deviceName = deviceName;
BlueManage().bludSendData(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 {
var status = await PermissionDialog.request(
Permission.storage, '需要访问读写权限才能使用手动升级固件'.tr);
if (status != true) {
return;
}
FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result == null || result.files.single.path is! String) {
return;
}
File file = File(result.files.single.path!);
Uint8List data = await file.readAsBytes();
headJson = await getHeadFile(data);
if (headJson is! Map) {
return;
}
otaBin = await checkFile(data, headJson!);
if (otaBin == null) {
return;
}
String md5Str = md5.convert(otaBin!).toString();
headJson!['fwMd5'] = md5Str;
blueOTAUpgrade(headJson!, [0, 0, 0, 0]);
}
//蓝牙操作 ota 升级
void blueOTAUpgrade(Map data, List<int> token) {
if (deviceName == null) {
AppLog.log('blueOTAUpgrade:设备名字为 null');
return;
}
BlueManage().bludSendData(deviceName!,
(BluetoothConnectionState deviceConnectionState) async {
if (deviceConnectionState == BluetoothConnectionState.connected) {
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());
showTitleEasyLoading("连接设备中...");
} else if (deviceConnectionState ==
BluetoothConnectionState.disconnected) {}
},isAddEquipment: true);
}
//循环传输升级固件包
Future<void> processOtaUpgrade() async {
if (!state.otaUpdateIng.value) {
return;
}
int length = otaBin?.length ?? 0;
if (otaCount == 0) {
//首次
int difference = length % 240;
otaCount = length ~/ 240 + (difference > 0 ? 1 : 0);
startSecond = DateTime.now().millisecondsSinceEpoch ~/ 1000;
}
if (otaCount <= otaIndex) {
int now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
String msg =
'传输完成 时间:${now - startSecond}秒 otaCount:$otaCount otaIndex:$otaIndex ';
closeOTADAta();
AppLog.log(msg);
// showToast(msg);
return;
}
int star = otaIndex * 240;
int end = (otaIndex + 1) * 240;
if (end > length) {
end = length;
}
int size = end - star;
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((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);
var meta = jsonDecode(metaStr);
if (meta is! Map) {
showToast('解析元数据失败,请选择正确的文件'.tr);
return null;
}
return meta..['metaLen'] = metaLen;
}
//检测升级文件并读取 bin
Future<Uint8List?> checkFile(Uint8List data, Map meta) async {
num binOffset = 16 + (meta['metaLen'] ?? 0);
// 获取固件数据部分
Uint8List bin = data.sublist(binOffset.toInt(), data.length);
//md5 校验有问题,暂时不解析
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;
}
@override
void onReady() {
super.onReady();
_initReplySubscription();
state.ifCurrentScreen.value = true;
startScanBlueList();
}
@override
void onInit() {
super.onInit();
}
@override
void onClose() {
super.onClose();
_replySubscription?.cancel();
}
}