app-starlock/lib/blue/blue_manage.dart
2024-09-29 10:05:29 +08:00

619 lines
23 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:io';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:flutter_bugly_plugin/flutter_bugly_plugin.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart';
import 'package:star_lock/app_settings/app_settings.dart';
import 'package:star_lock/tools/commonDataManage.dart';
import 'io_tool/io_model.dart';
import 'io_tool/io_tool.dart';
import 'io_tool/manager_event_bus.dart';
import 'reciver_data.dart';
//连接状态回调
typedef ConnectStateCallBack = Function(
BluetoothConnectionState connectionState);
typedef ScanDevicesCallBack = Function(List<ScanResult>);
class BlueManage {
factory BlueManage() => shareManager()!;
BlueManage._init() {
_initBlue();
}
final List<ScanResult> scanDevices = <ScanResult>[];
// 用来写入的服务id
final Guid _serviceIdConnect = Guid('fff0');
// 用来写入的服务id
final Guid _serviceIdWrite = Guid('0000FFF0-0000-1000-8000-00805F9B34FB');
// 用来订阅的特征id
final Guid _characteristicIdSubscription = Guid('fff1');
// 用来写入的特征id
final Guid _characteristicIdWrite = Guid('fff2');
// 监听发送事件
StreamSubscription<EventSendModel>? _sendStreamSubscription;
StreamSubscription<BluetoothConnectionState>? _connectionStateSubscription;
StreamSubscription<int>? _mtuSubscription;
int? _mtuSize = 20;
// 当前连接设备的名字
String connectDeviceName = '';
// 当前连接设备的mac地址
String connectDeviceMacAddress = '';
// 当前连接的设备
BluetoothDevice? bluetoothConnectDevice;
// 当前扫描到结果要连接设备
ScanResult? scanResult;
// 监听蓝牙连接状态
BluetoothConnectionState? bluetoothConnectionState =
BluetoothConnectionState.disconnected;
BluetoothAdapterState? _adapterState = BluetoothAdapterState.on;
StreamSubscription<BluetoothAdapterState>? _adapterStateStateSubscription;
// 听上报来的数据,参数来自前面扫描到的结果
List<int> allData = <int>[];
// 保存上一次的数据,用来判断是否收到重复的数据
List<int> lastTimeData = <int>[];
int? dataLen;
Timer? scanSingleTimer;
static BlueManage? _manager;
static BlueManage? shareManager() {
_manager ??= BlueManage._init();
// _manager!._initBlue();
return _manager;
}
BlueManage? get manager => shareManager();
void _initBlue() {
FlutterBluePlus.setLogLevel(LogLevel.error, color: true);
_initSendStreamSubscription();
_initAdapterStateStateSubscription();
}
void _initGetMtuSubscription() {
_mtuSubscription ??= bluetoothConnectDevice!.mtu.listen((int value) {
_mtuSize = value - 3;
AppLog.log('_mtuSizeValue:$value mtuSize:$_mtuSize');
});
}
void _initAdapterStateStateSubscription() {
_adapterStateStateSubscription ??=
FlutterBluePlus.adapterState.listen((BluetoothAdapterState state) {
AppLog.log('蓝牙状态:$state');
_adapterState = state;
});
}
void _initListenConnectionState() {
_connectionStateSubscription?.cancel();
_connectionStateSubscription = null;
_connectionStateSubscription = bluetoothConnectDevice!.connectionState
.listen((BluetoothConnectionState state) async {
bluetoothConnectionState = state;
AppLog.log('蓝牙连接回调状态:$state');
});
}
void _initSendStreamSubscription() {
_sendStreamSubscription ??= EventBusManager()
.eventBus!
.on<EventSendModel>()
.listen((EventSendModel model) {
if (model.sendChannel == DataChannel.ble) {
FlutterBluePlus.isSupported.then((bool isAvailable) async {
if (isAvailable) {
if (_adapterState == BluetoothAdapterState.on) {
// 蓝牙已开启,可以进行蓝牙操作
writeCharacteristicWithResponse(model.data);
} else {
try {} catch (e) {
AppLog.log('蓝牙打开失败');
}
}
} else {
AppLog.log('写入数据 蓝牙不可用,不能进行蓝牙操作');
}
});
}
});
}
/// 开始指定设备名称的扫描蓝牙设备
Future<void> startScanSingle(String deviceName, int timeout,
ScanDevicesCallBack scanDevicesCallBack) async {
final DateTime start = DateTime.now();
FlutterBluePlus.isSupported.then((bool isAvailable) async {
if (isAvailable) {
// AppLog.log('startScanSingle 蓝牙状态 系统蓝牙状态:$_adapterState 蓝牙连接状态:$bluetoothConnectionState');
if (_adapterState == BluetoothAdapterState.on) {
try {
//android 扫描比较慢,取样只要 3 分之一
final int divisor = Platform.isAndroid ? 3 : 1;
FlutterBluePlus.startScan(
continuousDivisor: divisor,
continuousUpdates: true,
withKeywords: <String>[deviceName],
timeout: Duration(seconds: timeout));
final Completer<dynamic> completer = Completer<dynamic>();
final StreamSubscription<List<ScanResult>> subscription =
FlutterBluePlus.scanResults.listen((List<ScanResult> results) {
final bool isExit = results.any((ScanResult element) =>
(element.device.platformName == deviceName) ||
(element.advertisementData.advName == deviceName));
final int milliseconds = DateTime.now().millisecondsSinceEpoch -
start.millisecondsSinceEpoch;
AppLog.log(
'扫描到的设备数:${results.length} 是否查找到 $isExit 以查找$milliseconds毫秒');
if (isExit) {
for (final ScanResult scanResult in results) {
if (((scanResult.advertisementData.serviceUuids.isNotEmpty
? scanResult.advertisementData.serviceUuids[0]
: '')
.toString()
.contains('758824')) &&
(scanResult.rssi >= -100)) {
// 查询id相同的元素
final int knownDeviceIndex = scanDevices.indexWhere(
(ScanResult d) =>
(d.device.platformName ==
scanResult.device.platformName) ||
(d.advertisementData.advName ==
scanResult.advertisementData.advName));
// 不存在的时候返回-1
if (knownDeviceIndex >= 0) {
scanDevices[knownDeviceIndex] = scanResult;
} else {
scanDevices.add(scanResult);
}
}
}
completer.complete();
}
}, onError: (e) {
AppLog.log(
'扫描失败:$e',
);
});
FlutterBluePlus.cancelWhenScanComplete(subscription);
await completer.future;
scanDevicesCallBack(scanDevices);
subscription.cancel();
} catch (e) {
AppLog.log('扫描失败');
}
} else {
try {
if (Platform.isAndroid) {
await FlutterBluePlus.turnOn();
}
} catch (e) {
AppLog.log('蓝牙打开失败');
}
}
} else {
AppLog.log('开始扫描 蓝牙不可用,不能进行蓝牙操作');
}
});
}
/// 开始扫描蓝牙设备
Future<void> startScan(int timeout, ScanDevicesCallBack scanDevicesCallBack,
{List<Guid>? idList}) async {
FlutterBluePlus.isSupported.then((bool isAvailable) async {
if (isAvailable) {
// AppLog.log('startScan 蓝牙状态 系统蓝牙状态:$_adapterState 蓝牙连接状态:$bluetoothConnectionState');
if (_adapterState == BluetoothAdapterState.on) {
try {
FlutterBluePlus.startScan(timeout: Duration(seconds: timeout));
final StreamSubscription<List<ScanResult>> subscription = FlutterBluePlus.scanResults.listen((List<ScanResult> results) {
scanDevices.clear();
for (final ScanResult scanResult in results) {
// AppLog.log('扫描到的设备:${scanResult.device.platformName} ${scanResult.advertisementData.advName} ${scanResult.rssi}');
// 判断名字为空的直接剔除
if (((scanResult.advertisementData.serviceUuids.isNotEmpty
? scanResult.advertisementData.serviceUuids[0]
: '')
.toString()
.contains('758824')) &&
(scanResult.rssi >= -100)) {
// 查询id相同的元素
final int knownDeviceIndex = scanDevices.indexWhere(
(ScanResult d) =>
(d.device.platformName ==
scanResult.device.platformName) ||
(d.advertisementData.advName ==
scanResult.advertisementData.advName));
// 不存在的时候返回-1
if (knownDeviceIndex >= 0) {
scanDevices[knownDeviceIndex] = scanResult;
} else {
scanDevices.add(scanResult);
}
}
}
scanDevicesCallBack(scanDevices);
}, onError: (e) {
AppLog.log(
'扫描失败:$e',
);
});
FlutterBluePlus.cancelWhenScanComplete(subscription);
} catch (e) {
AppLog.log('扫描失败');
}
} else {
try {
if (Platform.isAndroid) {
await FlutterBluePlus.turnOn();
}
} catch (e) {
AppLog.log('蓝牙打开失败');
}
}
} else {
AppLog.log('开始扫描 蓝牙不可用,不能进行蓝牙操作');
}
});
}
/// 调用发送数据 List senderData,
Future<void> blueSendData(
String deviceName, ConnectStateCallBack stateCallBack,
{bool isAddEquipment = false}) async {
FlutterBluePlus.isSupported.then((bool isAvailable) async {
if (isAvailable) {
// AppLog.log('蓝牙状态 系统蓝牙状态:$_adapterState 蓝牙连接状态:$bluetoothConnectionState');
if (_adapterState == BluetoothAdapterState.on) {
// 蓝牙已开启,可以进行蓝牙操作
if (bluetoothConnectionState != BluetoothConnectionState.connected) {
_connect(deviceName, (BluetoothConnectionState state) {
stateCallBack(bluetoothConnectionState!);
}, isAddEquipment: isAddEquipment);
} else {
stateCallBack(bluetoothConnectionState!);
}
} else {
try {
stateCallBack(BluetoothConnectionState.disconnected);
openBlue();
} catch (e) {
AppLog.log('蓝牙打开失败');
}
}
} else {
FlutterBuglyPlugin.reportException(exceptionName: 'blueSendData isAvailable状态', reason: 'blueSendData isAvailable不可用');
AppLog.log('开始扫描 蓝牙不可用,不能进行蓝牙操作');
}
});
}
/// 连接
Future<void> _connect(
String deviceName, ConnectStateCallBack connectStateCallBack,
{bool isAddEquipment = false}) async {
connectDeviceName = deviceName;
final List<ScanResult> devicesList = scanDevices;
final bool isExistDevice = isExistScanDevices(connectDeviceName);
final bool isCurrentDevice =
CommonDataManage().currentKeyInfo.lockName == deviceName;
final String? mac = CommonDataManage().currentKeyInfo.mac;
AppLog.log('开始连接 是否存在缓存:$isExistDevice 是否是当前设备:$isCurrentDevice mac$mac');
if (GetPlatform.isAndroid &&
!isExistDevice &&
isCurrentDevice &&
mac != null) {
scanSingleTimer?.cancel();
//兼容android 的低配手机
await doNotSearchBLE(mac, connectStateCallBack,
isAddEquipment: isAddEquipment);
scanSingleTimer = Timer(3.seconds, () {
scanSingleTimer?.cancel();
startScanSingle(deviceName, 15, (List<ScanResult> scanDevices) => null);
});
} else if (isAddEquipment == false && isExistDevice == false) {
//取消缓存直接使用,存在配对场景设备信息会更变
// AppLog.log('无存在设备需要扫描 deviceName:$deviceName isAddEquipment:$isAddEquipment');
startScanSingle(deviceName, 15, (List<ScanResult> scanDevices) {
_connectDevice(scanDevices, deviceName, connectStateCallBack,
isAddEquipment: isAddEquipment);
});
} else {
// AppLog.log('安卓或者iOS 存在设备不需要扫描 deviceName:$deviceName isAddEquipment:$isAddEquipment');
_connectDevice(devicesList, deviceName, connectStateCallBack,
isAddEquipment: isAddEquipment);
}
}
//查找缓存里面是否有设备
bool isExistScanDevices(String connectDeviceName) {
final bool isExistDevice = scanDevices.any((ScanResult element) =>
element.device.platformName == connectDeviceName ||
element.advertisementData.advName == connectDeviceName);
return isExistDevice;
}
Future<void> _connectDevice(
List<ScanResult> devicesList,
String deviceName,
ConnectStateCallBack connectStateCallBack,
{
bool isAddEquipment = false,// 是否是添加设备之前
bool isReconnect = true,// 是否是重连
}) async {
// 判断数组列表里面是否有这个设备
// AppLog.log("devicesList:$devicesList");
final int knownDeviceIndex = devicesList.indexWhere((ScanResult d) =>
(d.device.platformName == deviceName) ||
(d.advertisementData.advName == deviceName));
ScanResult? scanResult; //使用局部变量防止出现缓存
if (knownDeviceIndex >= 0) {
// 存在的时候赋值
connectDeviceMacAddress =
devicesList[knownDeviceIndex].advertisementData.advName.isNotEmpty
? devicesList[knownDeviceIndex].advertisementData.advName
: devicesList[knownDeviceIndex].device.platformName;
bluetoothConnectDevice = devicesList[knownDeviceIndex].device;
scanResult = devicesList[knownDeviceIndex];
// AppLog.log('bluetoothConnectDevice: $bluetoothConnectDevice scanResult:$scanResult');
_initGetMtuSubscription();
_initListenConnectionState();
}
if (scanResult == null || connectDeviceMacAddress.isEmpty) {
return;
}
AppLog.log('调用了停止扫描的方法');
await stopScan();
if ((scanResult.advertisementData.serviceUuids[0].toString()[31] == '0') && isAddEquipment == false) {
// 添加这个判断是因为有些苹果设备或者安卓等性能比较好的设备时,添加完锁之后,锁板未改变为已添加状态之前,就进行了蓝牙连接,导致添加完锁就失败,这里进行了判断,如果第一次连接失败,就清除缓存重新扫描连接
if(isReconnect == true){
AppLog.log('该锁已被重置, 重新发送扫描命令');
scanDevices.clear();
startScanSingle(deviceName, 15, (List<ScanResult> scanDevices) {
_connectDevice(scanDevices, deviceName, connectStateCallBack, isAddEquipment: isAddEquipment, isReconnect: false);
});
}else{
connectStateCallBack(BluetoothConnectionState.disconnected);
EasyLoading.showToast('该锁已被重置'.tr, duration: 2000.milliseconds);
scanDevices.clear();
}
return;
}
//尝试连接设备
await bluetoothDeviceConnect(bluetoothConnectDevice!, connectStateCallBack);
}
//直接给蓝牙设备写入
Future<void> doNotSearchBLE(
String masAdds, ConnectStateCallBack connectStateCallBack,
{bool isAddEquipment = false}) async {
await FlutterBluePlus.stopScan();
if (bluetoothConnectDevice == null ||
bluetoothConnectDevice?.remoteId.str != masAdds) {
bluetoothConnectDevice = BluetoothDevice.fromId(masAdds);
_initGetMtuSubscription();
_initListenConnectionState();
}
//尝试连接设备
await bluetoothDeviceConnect(bluetoothConnectDevice!, connectStateCallBack,
isAddEquipment: isAddEquipment);
}
//设备连接
Future<void> bluetoothDeviceConnect(BluetoothDevice bluetoothConnectDevice,
ConnectStateCallBack connectStateCallBack,
{bool isAddEquipment = false}) async {
// 重连三次
const int maxAttempts = 3;
int attempt = 0;
while (attempt < maxAttempts) {
try {
await bluetoothConnectDevice.connect(timeout: 5.seconds);
break; // If the connection is successful, break the loop
} catch (e) {
AppLog.log('连接失败 重连了: $e');
attempt++; // Increase the attempt count
if (attempt < maxAttempts) {
AppLog.log('重新尝试连接...');
}
}
}
if (attempt >= maxAttempts) {
AppLog.log('$maxAttempts次后尝试连接失败');
connectStateCallBack(BluetoothConnectionState.disconnected);
}
if (bluetoothConnectionState == BluetoothConnectionState.connected) {
try {
final List<BluetoothService> services =
await bluetoothConnectDevice.discoverServices();
//循环判断服务
for (final BluetoothService service in services) {
if (service.uuid == _serviceIdConnect) {
for (final BluetoothCharacteristic characteristic in service.characteristics) {
if (characteristic.characteristicUuid == _characteristicIdSubscription) {
_subScribeToCharacteristic(characteristic);
bluetoothConnectionState = BluetoothConnectionState.connected;
connectStateCallBack(bluetoothConnectionState!);
}
}
}
}
} on Exception catch (e) {
bluetoothConnectionState = BluetoothConnectionState.disconnected;
connectStateCallBack(bluetoothConnectionState!);
AppLog.log(
'发现设备时失败 e:$e bluetoothConnectionState:$bluetoothConnectionState');
rethrow;
}
}
}
Future<void> _subScribeToCharacteristic(
BluetoothCharacteristic characteristic) async {
final StreamSubscription<List<int>> subscription =
characteristic.onValueReceived.listen((List<int> data) {
AppLog.log('订阅获取的数据: $data ');
if (data == lastTimeData || data.isEmpty) {
return;
} else {
lastTimeData = data;
}
final bool dataHeadCorrect = isDataHeadCorrect(data);
final bool allDataHeadCorrect = isDataHeadCorrect(allData);
if (dataHeadCorrect && allDataHeadCorrect) {
//缓存数据和新数据都有包头,直接放弃缓存数据
allData = <int>[];
}
if (dataHeadCorrect) {
// 当包有头时
// 判断是否需要分包
dataLen = data[8] * 256 + data[9]; // 高16位用来指示后面数据块内容的长度
if (dataLen! + 14 > data.length) {
// 当前包的长度小于实际的包时 分包添加 不解析
allData.addAll(data);
} else {
// 当前包的长度小于实际的包时 不分包 解析
allData.addAll(data);
CommandReciverManager.appDataReceive(allData);
// 发送完解析初始化数组
allData = <int>[];
}
} else if (allDataHeadCorrect) {
// 当包没有头时 是分包的包 直接添加
allData.addAll(data);
if (((dataLen ?? 0) + 14) <= allData.length) {
// 当长度小于等于当前包的数据时 直接解析数据
CommandReciverManager.appDataReceive(allData);
// 发送完解析初始化数组
allData = <int>[];
}
}
});
bluetoothConnectDevice!.cancelWhenDisconnected(subscription);
await characteristic.setNotifyValue(true);
}
//判断数据头是否正确
bool isDataHeadCorrect(List<int> data) {
if (data.length < 4) {
return false;
}
//239, 1, 238, 2, 是数据包头
if ((data[0] == 0xEF) &&
(data[1] == 0x01) &&
(data[2] == 0xEE) &&
(data[3] == 0x02)) {
return true;
} else {
return false;
}
}
// 写入
Future<void> writeCharacteristicWithResponse(List<int> value) async {
final List<BluetoothService> services =
await bluetoothConnectDevice!.discoverServices();
for (final BluetoothService service in services) {
if (service.uuid == _serviceIdConnect) {
for (final BluetoothCharacteristic characteristic
in service.characteristics) {
if (characteristic.characteristicUuid == _characteristicIdWrite) {
try {
final List<int> valueList = value;
final List subData = splitList(valueList, _mtuSize!);
// AppLog.log('writeCharacteristicWithResponse _mtuSize:$_mtuSize 得到的分割数据:$subData');
for (int i = 0; i < subData.length; i++) {
if (characteristic.properties.writeWithoutResponse) {
// 使用WRITE_NO_RESPONSE属性写入值
await characteristic.write(subData[i], withoutResponse: true);
} else if (characteristic.properties.write) {
// 使用WRITE属性写入值
await characteristic.write(subData[i]);
} else {
// 特性不支持写入
throw Exception(
'This characteristic does not support writing.');
}
}
} on Exception catch (e, s) {
AppLog.log('APP写入失败: $e $s');
rethrow;
}
}
}
}
}
}
// 停止扫描蓝牙设备
Future<void> stopScan() async {
try {
await FlutterBluePlus.stopScan();
} catch (e) {
AppLog.log('停止扫描失败');
}
}
// 断开连接
Future<void> disconnect() async {
try {
connectDeviceMacAddress = '';
if (bluetoothConnectionState == BluetoothConnectionState.connected) {
//加快蓝牙断连
await bluetoothConnectDevice!.disconnect(timeout: 3);
AppLog.log('断开连接成功');
}
} on Exception catch (e, _) {
AppLog.log('断开连接失败: $e');
} finally {
bluetoothConnectionState = BluetoothConnectionState.disconnected;
}
}
Future<void> openBlue() async {
if (Platform.isAndroid) {
await FlutterBluePlus.turnOn();
}
if (Platform.isIOS) {
EasyLoading.showToast('请开启蓝牙'.tr, duration: 2000.milliseconds);
}
}
void disposed() {
_sendStreamSubscription?.cancel();
_mtuSubscription!.cancel();
_adapterStateStateSubscription!.cancel();
_connectionStateSubscription!.cancel();
}
}