1104 lines
34 KiB
Dart
1104 lines
34 KiB
Dart
import 'dart:async';
|
||
import 'dart:convert';
|
||
import 'dart:io';
|
||
import 'dart:typed_data';
|
||
|
||
import 'package:ef/ef.dart';
|
||
import 'package:flutter/foundation.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||
import 'package:permission_handler/permission_handler.dart';
|
||
import 'package:vbvs_app/common/util/MyUtils.dart';
|
||
import 'package:vbvs_app/pages/common/selectDialog.dart';
|
||
|
||
String findInput = "";
|
||
double rssichange = -90;
|
||
Function? findCall;
|
||
|
||
int mcuMax = 500;
|
||
|
||
String myuuid = "00000001-0000-1000-8000-00805F9B34FB";
|
||
|
||
int closeTime = 20000;
|
||
|
||
Map devices = Map();
|
||
|
||
StreamSubscription<BluetoothAdapterState>? subscription_adapterState;
|
||
|
||
Map<String, ConnectedDeviceProp> connectList =
|
||
Map<String, ConnectedDeviceProp>();
|
||
|
||
bool isBleStart = false;
|
||
|
||
StreamSubscription<List<ScanResult>>? onScanResultsListen;
|
||
|
||
StreamSubscription<OnConnectionStateChangedEvent>?
|
||
streamSubscription_onConnectionStateChangedEvent;
|
||
|
||
Timer? showToastTimer;
|
||
|
||
List permissionInfo = [
|
||
["位置权限说明", "获得位置信息,连接附近的蓝牙设备与推荐附近门店"],
|
||
["蓝牙权限说明", "搜索链接附近的蓝牙设备"],
|
||
["附近设备权限说明", "搜索链接附近的蓝牙设备"]
|
||
];
|
||
|
||
bool isQuanShiDevice(name) {
|
||
return "$name".contains("S4-ZM-M94-4") || "$name".contains("S4-ZM-N94-4") || "$name".contains("MHT-SWES-D");
|
||
}
|
||
|
||
bool isMHTSWES(name) {
|
||
return "$name".contains("MHT-SWES-H") || "$name".contains("MHT-SWES-M") || "$name".contains("MHT-SWES-S");
|
||
}
|
||
|
||
bleParse() {
|
||
findCall = null;
|
||
print("bleParse 执行了");
|
||
showToastTimer?.cancel();
|
||
if (FlutterBluePlus.isScanningNow) {
|
||
FlutterBluePlus.stopScan();
|
||
}
|
||
}
|
||
|
||
start(Function fun, {Function? bleOnCall}) async {
|
||
if (isBleStart) {
|
||
var isOk = await requestBluetoothPermission();
|
||
if (isOk == true) {
|
||
findCall = fun;
|
||
bleOnCall?.call();
|
||
bleOnCall = null;
|
||
}
|
||
print("ble start again");
|
||
return;
|
||
}
|
||
print("ble start");
|
||
FlutterBluePlus.setLogLevel(LogLevel.info, color: false);
|
||
streamSubscription_onConnectionStateChangedEvent?.cancel();
|
||
streamSubscription_onConnectionStateChangedEvent =
|
||
FlutterBluePlus.events.onConnectionStateChanged.listen((event) {
|
||
print('${event.device} ${event.connectionState}');
|
||
});
|
||
if (await FlutterBluePlus.isSupported == false) {
|
||
print("Bluetooth not supported by this device");
|
||
return;
|
||
}
|
||
|
||
// handle bluetooth on & off
|
||
// note: for iOS the initial state is typically BluetoothAdapterState.unknown
|
||
// note: if you have permissions issues you will get stuck at BluetoothAdapterState.unauthorized
|
||
subscription_adapterState?.cancel();
|
||
int callIndex = 0;
|
||
subscription_adapterState =
|
||
FlutterBluePlus.adapterState.listen((BluetoothAdapterState state) async {
|
||
print(state);
|
||
print("蓝牙状态 $state");
|
||
if (state == BluetoothAdapterState.on) {
|
||
showToastTimer?.cancel();
|
||
// usually start scanning, connecting, etc
|
||
findCall = fun;
|
||
var isOk = await requestBluetoothPermission();
|
||
if (isOk == true) {
|
||
bleOnCall?.call();
|
||
bleOnCall = null;
|
||
}
|
||
} else {
|
||
// show an error to the user, etc
|
||
if (Platform.isIOS &&
|
||
callIndex == 0 &&
|
||
state == BluetoothAdapterState.unknown) {
|
||
callIndex++;
|
||
return;
|
||
}
|
||
if (Platform.isAndroid) {
|
||
showToast("请打开蓝牙开关");
|
||
}
|
||
if (Platform.isIOS) {
|
||
showToast("请打开蓝牙开关且开启蓝牙权限");
|
||
await showCustomConfirmAndCancelDialog(
|
||
Get.context!, "请在“设置-蓝牙”中打开蓝牙开关或者在“设置-APP”中找到对应APP开启蓝牙权限",
|
||
confirmName: "去设置")
|
||
.then((msg) async {
|
||
if (msg == "confirm") {
|
||
openAppSettings();
|
||
}
|
||
});
|
||
}
|
||
isBleStart = false;
|
||
showToastTimer?.cancel();
|
||
showToastTimer = Timer.periodic(const Duration(seconds: 5), (t) {
|
||
if (t.tick > 3) {
|
||
t.cancel();
|
||
}
|
||
if (Platform.isAndroid) {
|
||
showToast("请打开蓝牙开关");
|
||
}
|
||
if (Platform.isIOS) {
|
||
showToast("请打开蓝牙开关且开启蓝牙权限");
|
||
}
|
||
});
|
||
}
|
||
callIndex++;
|
||
});
|
||
|
||
// turn on bluetooth ourself if we can
|
||
// for iOS, the user controls bluetooth enable/disable
|
||
if (Platform.isAndroid &&
|
||
FlutterBluePlus.adapterStateNow != BluetoothAdapterState.on) {
|
||
showPermissionInfoDialog(Get.context!, permissionInfo);
|
||
FlutterBluePlus.turnOn().then((e) {
|
||
Get.back();
|
||
}).catchError((e) {
|
||
Get.back();
|
||
});
|
||
}
|
||
|
||
var timer = null;
|
||
onScanResultsListen?.cancel();
|
||
onScanResultsListen = FlutterBluePlus.onScanResults.listen(
|
||
(List<ScanResult> results) {
|
||
// print(results.length);
|
||
for (ScanResult result in results) {
|
||
// if (result.device.id.toString().contains("A3:76")) {
|
||
// print("$result");
|
||
// }
|
||
Map d = {
|
||
"updateTime": DateTime.now().millisecondsSinceEpoch,
|
||
"name": result.device.advName,
|
||
"id": result.device.remoteId.str,
|
||
"rssi": result.rssi,
|
||
"device": result.device,
|
||
"connectable": result.advertisementData.connectable
|
||
};
|
||
Map<int, List<int>> m_d = result.advertisementData.manufacturerData;
|
||
m_d.keys.toList().forEach((v) {
|
||
if (v == 65517 && m_d[65517]?.length != 0) {
|
||
List<int> a = [0, 0, ...?m_d[65517]];
|
||
advertisDataFormatter(a, d);
|
||
} else if (v == 11125 && m_d[11125]?.length == 8) {
|
||
List<int> a = [...?m_d[11125]];
|
||
d['adData'] = {'deviceId': ab2str(a.sublist(2, 8)).toUpperCase()};
|
||
} else if (m_d[v]?.length == 8 && isQuanShiDevice(d["name"])) {
|
||
List<int> a = [...?m_d[v]];
|
||
d['adData'] = {'deviceId': ab2str(a.sublist(2, 8)).toUpperCase()};
|
||
} else if (m_d[v]?.length == 4 && isMHTSWES(d["name"])) {
|
||
ByteData bd = ByteData(2);
|
||
bd.setUint16(0, v, Endian.little);
|
||
List<int> a = [bd.getUint8(0), bd.getUint8(1), ...?m_d[v]];
|
||
d['adData'] = {'deviceId': ab2str(a).toUpperCase()};
|
||
} else if (m_d[v]?.length == 6 && isMHTSWES(d["name"])) {
|
||
List<int> a = [...?m_d[v]];
|
||
d['adData'] = {'deviceId': ab2str(a).toUpperCase()};
|
||
}
|
||
});
|
||
devices[d['id']] = d;
|
||
// print('Device found: ${result.device.name}, ${result.device.id}');
|
||
if (timer == null) {
|
||
timer = 1;
|
||
timer = Future.delayed(const Duration(microseconds: 300), () {
|
||
timer = null;
|
||
find();
|
||
});
|
||
}
|
||
}
|
||
},
|
||
onError: (e) => print(e),
|
||
);
|
||
}
|
||
|
||
// Future<Position?> locationCheck({bool isGetLocation = true}) async {
|
||
// // 先查看定位服务是否开启
|
||
// bool b = await Geolocator.isLocationServiceEnabled();
|
||
// if (b == false) {
|
||
// if (Platform.isAndroid) {
|
||
// showToast("请开启系统位置开关", closeTime: 5);
|
||
// await showCustomConfirmAndCancelDialog(Get.context!, "请开启系统位置开关",
|
||
// confirmName: "去设置")
|
||
// .then((msg) async {
|
||
// if (msg == "confirm") {
|
||
// // await Geolocator.openLocationSettings();
|
||
// await openGeolocatorLocationSettingsAndWait();
|
||
// b = await Geolocator.isLocationServiceEnabled();
|
||
// }
|
||
// });
|
||
// }
|
||
// if (Platform.isIOS) {
|
||
// showToast("请开启系统定位服务与定位权限", closeTime: 5);
|
||
// await showCustomConfirmAndCancelDialog(Get.context!,
|
||
// "请在“设置-隐私与安全性-定位服务”中开启定位服务开关或者在“设置-APP”中找到对应APP开启定位服务权限",
|
||
// confirmName: "去设置")
|
||
// .then((msg) async {
|
||
// if (msg == "confirm") {
|
||
// // await Geolocator.openAppSettings();
|
||
// await openGeolocatorAppSettingsAndWait();
|
||
// b = await Geolocator.isLocationServiceEnabled();
|
||
// }
|
||
// });
|
||
// }
|
||
// }
|
||
|
||
// var permission = await Geolocator.checkPermission();
|
||
// if (permission == LocationPermission.denied ||
|
||
// permission == LocationPermission.deniedForever) {
|
||
// showPermissionInfoDialog(Get.context!, [permissionInfo[0]]);
|
||
// permission = await Geolocator.requestPermission().catchError((e) {
|
||
// Get.back();
|
||
// });
|
||
// Get.back();
|
||
// if (permission == LocationPermission.deniedForever) {
|
||
// if (Platform.isAndroid) {
|
||
// await showCustomConfirmAndCancelDialog(Get.context!, "请开启位置权限(打开精确位置)",
|
||
// confirmName: "去设置")
|
||
// .then((msg) async {
|
||
// if (msg == "confirm") {
|
||
// // await Geolocator.openAppSettings();
|
||
// await openGeolocatorAppSettingsAndWait();
|
||
// }
|
||
// });
|
||
// }
|
||
// if (Platform.isIOS) {
|
||
// await showCustomConfirmAndCancelDialog(Get.context!,
|
||
// "请在“设置-隐私与安全性-定位服务”中开启定位服务开关或者在“设置-APP”中找到对应APP开启定位服务权限",
|
||
// confirmName: "去设置")
|
||
// .then((msg) async {
|
||
// if (msg == "confirm") {
|
||
// // await Geolocator.openAppSettings();
|
||
// await openGeolocatorAppSettingsAndWait();
|
||
// }
|
||
// });
|
||
// }
|
||
// }
|
||
// }
|
||
|
||
// Position? position;
|
||
// if (isGetLocation) {
|
||
// if (b &&
|
||
// permission != LocationPermission.denied &&
|
||
// permission != LocationPermission.deniedForever) {
|
||
// try {
|
||
// position = await Geolocator.getCurrentPosition(
|
||
// locationSettings:
|
||
// const LocationSettings(timeLimit: Duration(seconds: 5)));
|
||
// print("$position");
|
||
// } catch (e) {
|
||
// print("error $e");
|
||
// }
|
||
// }
|
||
// }
|
||
// return position;
|
||
// }
|
||
|
||
/// 使用 Completer 来等待用户返回应用,自定义的 LifecycleEventHandler 类将监视应用程序的生命周期状态,
|
||
/// 并在应用程序恢复时完成,有效地等待用户从设置屏幕返回
|
||
/// ------开始-----
|
||
Future<void> openGeolocatorAppSettingsAndWait() async {
|
||
final geolocatorAppSettingsLifecycleState =
|
||
GeolocatorAppSettingsLifecycleEventHandler();
|
||
|
||
WidgetsBinding.instance.addObserver(geolocatorAppSettingsLifecycleState);
|
||
|
||
// await Geolocator.openAppSettings();
|
||
|
||
await geolocatorAppSettingsLifecycleState.waitForResume();
|
||
|
||
WidgetsBinding.instance.removeObserver(geolocatorAppSettingsLifecycleState);
|
||
|
||
print('AppSettings have been opened and user has returned');
|
||
}
|
||
|
||
class GeolocatorAppSettingsLifecycleEventHandler
|
||
extends WidgetsBindingObserver {
|
||
final Completer<void> _completer = Completer<void>();
|
||
|
||
@override
|
||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||
if (state == AppLifecycleState.resumed && !_completer.isCompleted) {
|
||
_completer.complete();
|
||
}
|
||
}
|
||
|
||
Future<void> waitForResume() => _completer.future;
|
||
}
|
||
|
||
/// 使用 Completer 来等待用户返回应用,自定义的 LifecycleEventHandler 类将监视应用程序的生命周期状态,
|
||
/// 并在应用程序恢复时完成,有效地等待用户从设置屏幕返回
|
||
/// ------结束-----
|
||
|
||
/// 使用 Completer 来等待用户返回应用,自定义的 LifecycleEventHandler 类将监视应用程序的生命周期状态,
|
||
/// 并在应用程序恢复时完成,有效地等待用户从设置屏幕返回
|
||
/// ------开始-----
|
||
Future<void> openGeolocatorLocationSettingsAndWait() async {
|
||
final geolocatorLocationSettingsLifecycleState =
|
||
GeolocatorLocationSettingsLifecycleEventHandler();
|
||
|
||
WidgetsBinding.instance.addObserver(geolocatorLocationSettingsLifecycleState);
|
||
|
||
// await Geolocator.openLocationSettings();
|
||
|
||
await geolocatorLocationSettingsLifecycleState.waitForResume();
|
||
|
||
WidgetsBinding.instance
|
||
.removeObserver(geolocatorLocationSettingsLifecycleState);
|
||
|
||
print('LocationSettings have been opened and user has returned');
|
||
}
|
||
|
||
class GeolocatorLocationSettingsLifecycleEventHandler
|
||
extends WidgetsBindingObserver {
|
||
final Completer<void> _completer = Completer<void>();
|
||
|
||
@override
|
||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||
if (state == AppLifecycleState.resumed && !_completer.isCompleted) {
|
||
_completer.complete();
|
||
}
|
||
}
|
||
|
||
Future<void> waitForResume() => _completer.future;
|
||
}
|
||
|
||
/// 使用 Completer 来等待用户返回应用,自定义的 LifecycleEventHandler 类将监视应用程序的生命周期状态,
|
||
/// 并在应用程序恢复时完成,有效地等待用户从设置屏幕返回
|
||
/// ------结束-----
|
||
|
||
Future requestBluetoothPermission() async {
|
||
if (Platform.isIOS) {
|
||
PermissionStatus isBleGranted = await Permission.bluetooth.request();
|
||
print('checkBlePermissions-ios, isBleGranted=$isBleGranted');
|
||
if (isBleGranted.isGranted) {
|
||
startBluetoothScanning();
|
||
return true;
|
||
} else {
|
||
showToast("蓝牙开关或蓝牙权限未开启,请开启蓝牙开关与蓝牙权限", closeTime: 7);
|
||
await showCustomConfirmAndCancelDialog(
|
||
Get.context!, "请“设置-蓝牙”中打开蓝牙开关或者在“设置-APP”中找到对应APP开启蓝牙权限",
|
||
confirmName: "去设置")
|
||
.then((msg) async {
|
||
if (msg == "confirm") {
|
||
openAppSettings();
|
||
}
|
||
});
|
||
return false;
|
||
}
|
||
} else if (Platform.isAndroid) {
|
||
// 检查蓝牙扫描权限
|
||
String error = "";
|
||
bool isShowDialog = false;
|
||
if (!await Permission.bluetoothScan.isGranted) {
|
||
if (!isShowDialog) {
|
||
isShowDialog = true;
|
||
showPermissionInfoDialog(Get.context!, permissionInfo);
|
||
}
|
||
PermissionStatus status = await Permission.bluetoothScan.request();
|
||
if (!status.isGranted) {
|
||
error += "蓝牙扫描权限未开启,请开启附近设备权限";
|
||
}
|
||
print("蓝牙扫描 $status");
|
||
}
|
||
|
||
// 检查蓝牙连接权限
|
||
if (!await Permission.bluetoothConnect.isGranted) {
|
||
if (!isShowDialog) {
|
||
isShowDialog = true;
|
||
showPermissionInfoDialog(Get.context!, permissionInfo);
|
||
}
|
||
PermissionStatus status = await Permission.bluetoothConnect.request();
|
||
if (!status.isGranted) {
|
||
if (error.isNotEmpty) {
|
||
error += "\n";
|
||
}
|
||
error += "蓝牙连接权限未开启,请开启蓝牙权限";
|
||
}
|
||
print("蓝牙连接 $status");
|
||
}
|
||
|
||
// 检查位置权限
|
||
if (!await Permission.location.isGranted) {
|
||
if (!isShowDialog) {
|
||
isShowDialog = true;
|
||
showPermissionInfoDialog(Get.context!, permissionInfo);
|
||
}
|
||
//检查
|
||
PermissionStatus status = await Permission.location.request();
|
||
print("位置权限 $status");
|
||
if (!status.isGranted) {
|
||
await showCustomConfirmAndCancelDialog(Get.context!, "请开启位置权限(打开精确位置)",
|
||
confirmName: "去设置")
|
||
.then((msg) async {
|
||
if (msg == "confirm") {
|
||
await openGeolocatorAppSettingsAndWait();
|
||
// await Future.delayed(const Duration(seconds: 2));
|
||
print('Proceeding with other operations');
|
||
status = await Permission.location.request();
|
||
}
|
||
});
|
||
}
|
||
if (!status.isGranted) {
|
||
if (error.isNotEmpty) {
|
||
error += "\n";
|
||
}
|
||
error += "位置权限未开启,请开启位置权限";
|
||
}
|
||
}
|
||
|
||
if (isShowDialog) {
|
||
Get.back();
|
||
}
|
||
|
||
if (await Permission.bluetoothScan.isGranted &&
|
||
await Permission.bluetoothConnect.isGranted &&
|
||
await Permission.location.isGranted) {
|
||
// bool b = await Geolocator.isLocationServiceEnabled();
|
||
bool b =false;
|
||
if (b == false) {
|
||
await showCustomConfirmAndCancelDialog(Get.context!, "请开启系统位置开关",
|
||
confirmName: "去设置")
|
||
.then((msg) async {
|
||
if (msg == "confirm") {
|
||
// bool isOpen = await Geolocator.openLocationSettings();
|
||
await openGeolocatorLocationSettingsAndWait();
|
||
// await Future.delayed(const Duration(seconds: 2));
|
||
print('Proceeding with other operations');
|
||
// b = await Geolocator.isLocationServiceEnabled();
|
||
}
|
||
});
|
||
}
|
||
if (b) {
|
||
isBleStart = true;
|
||
startBluetoothScanning();
|
||
return true;
|
||
} else {
|
||
showToast("系统位置开关未开启,请开启系统位置开关", closeTime: 7);
|
||
return false;
|
||
}
|
||
} else {
|
||
Timer(Duration.zero, () async {
|
||
showToast(error, closeTime: 7);
|
||
});
|
||
return false;
|
||
}
|
||
} else {
|
||
showToast("当前系统不支持蓝牙,无法使用此功能", closeTime: 7);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
void find() {
|
||
int len = devices.length;
|
||
String reg = findInput.toLowerCase().replaceAll(RegExp("[::]"), "");
|
||
List list = devices.values.toList();
|
||
for (int i = 0; i < len; i++) {
|
||
Map d = list[i];
|
||
bool flag = d['rssi'] >= rssichange;
|
||
if (flag) {
|
||
bool a = d['name'].toString().toLowerCase().contains(reg);
|
||
a = a ||
|
||
d['id']
|
||
.toString()
|
||
.toLowerCase()
|
||
.replaceAll(RegExp("[::]"), "")
|
||
.contains(reg);
|
||
// if(d.adData && d.adData.deviceId) {
|
||
// a = a || d.adData.deviceId.toLowerCase().replace(/[::]/g, "").indexOf(reg) > -1
|
||
// }
|
||
// if(d.adData && d.adData.version && reg) {
|
||
// a = a || (d.adData.version + "").indexOf(reg) > -1
|
||
// }
|
||
flag = flag && a;
|
||
}
|
||
if (flag) {
|
||
flag = flag &&
|
||
DateTime.now().millisecondsSinceEpoch - d['updateTime'] < closeTime;
|
||
}
|
||
if (!flag) {
|
||
d['isClose'] = true;
|
||
} else {
|
||
d['isClose'] = false;
|
||
}
|
||
}
|
||
var result = list.where((item) => item['isClose'] == false).toList();
|
||
if (result == null) {
|
||
findCall?.call([]);
|
||
} else {
|
||
// print(result);
|
||
result.sort((a, b) {
|
||
// print("${a['rssi']},${b['rssi']}");
|
||
return b['rssi'] - a['rssi'];
|
||
});
|
||
if (result.length > 0) {
|
||
findCall?.call(result.where((d) => d?['adData'] != null).toList());
|
||
} else {
|
||
findCall?.call(result);
|
||
}
|
||
}
|
||
}
|
||
|
||
String ab2str(List<int> buffer) {
|
||
return buffer.map((x) => x.toRadixString(16).padLeft(2, '0')).join('');
|
||
}
|
||
|
||
void advertisDataFormatter(var a, item) {
|
||
Map<String, dynamic> obj = {};
|
||
try {
|
||
if (a[2] == 1) {
|
||
obj['sn'] = a[3];
|
||
obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase();
|
||
obj['b'] = a[10];
|
||
obj['h'] = a[11];
|
||
obj['t'] = a[12];
|
||
item['adData'] = obj;
|
||
} else if (a[2] == 2) {
|
||
obj['sn'] = a[3];
|
||
obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase();
|
||
obj['b'] = a[10];
|
||
obj['h'] = a[11];
|
||
obj['t'] = a[12];
|
||
obj['net'] = (a[13] & 1) == 1 ? '在线' : '离线';
|
||
obj['flag'] = (a[13] & 2) == 2 ? '异常' : '正常';
|
||
ByteData byteData = ByteData.sublistView(
|
||
Uint8List.fromList(a.sublist(14, 18).reversed.toList()));
|
||
obj['version'] = byteData.getUint32(0);
|
||
item['adData'] = obj;
|
||
} else if (a[2] == 3) {
|
||
List<String> otherstr = [];
|
||
obj['sn'] = a[3];
|
||
obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase();
|
||
obj['b'] = a[10];
|
||
obj['h'] = a[11];
|
||
obj['t'] = a[12];
|
||
obj['net'] = (a[13] & 1) == 1 ? '在线' : '离线';
|
||
obj['flag'] = (a[13] & 2) == 2 ? '异常' : '正常';
|
||
|
||
if ((a[13] & 4) == 4) {
|
||
otherstr.add('呼吸暂停');
|
||
}
|
||
|
||
if ((a[13] & 8) == 8 && (a[13] & 1) == 1) {
|
||
obj['isbed'] = '在床';
|
||
} else {
|
||
obj['isbed'] = '离床';
|
||
}
|
||
|
||
if ((a[13] & 16) == 16) {
|
||
otherstr.add('授权过期');
|
||
}
|
||
|
||
if ((a[13] & 64) == 64) {
|
||
otherstr.add('设备休眠');
|
||
}
|
||
|
||
obj['other'] = otherstr.join('、');
|
||
|
||
ByteData byteData = ByteData.sublistView(
|
||
Uint8List.fromList(a.sublist(14, 18).reversed.toList()));
|
||
obj['version'] = byteData.getUint32(0);
|
||
|
||
ByteData qsnData =
|
||
ByteData.sublistView(Uint8List.fromList(a.sublist(17, 19)));
|
||
obj['qsn'] = qsnData.getUint16(0) * 256 + obj['sn'];
|
||
|
||
item['adData'] = obj;
|
||
} else if (a.length > 17) {
|
||
obj['sn'] = a[3];
|
||
obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase();
|
||
obj['b'] = a[10];
|
||
obj['h'] = a[11];
|
||
obj['t'] = a[12];
|
||
obj['net'] = (a[13] & 1) == 1 ? '在线' : '离线';
|
||
obj['flag'] = (a[13] & 2) == 2 ? '异常' : '正常';
|
||
|
||
ByteData byteData = ByteData.sublistView(
|
||
Uint8List.fromList(a.sublist(14, 18).reversed.toList()));
|
||
obj['version'] = byteData.getUint32(0);
|
||
|
||
item['adData'] = obj;
|
||
}
|
||
} catch (e) {
|
||
print(e);
|
||
}
|
||
}
|
||
|
||
void startBluetoothScanning() async {
|
||
// 开始扫描附近的蓝牙设备
|
||
if (FlutterBluePlus.isScanningNow) {
|
||
await FlutterBluePlus.stopScan();
|
||
}
|
||
FlutterBluePlus.startScan(timeout: const Duration(seconds: 15));
|
||
}
|
||
|
||
getOneConnectedDeviceProp(id) {
|
||
return connectList[id];
|
||
}
|
||
|
||
void setOther(device, connectedDeviceProp, fun) async {
|
||
try {
|
||
List<BluetoothService> services = await device.discoverServices();
|
||
print(services);
|
||
bool isNotify = false;
|
||
bool isWrite = false;
|
||
for (var service in services) {
|
||
if (connectedDeviceProp.connectedDevicePropType ==
|
||
ConnectedDevicePropType.JunHe) {
|
||
if (service.uuid.str128.toUpperCase() != myuuid) {
|
||
continue;
|
||
}
|
||
}
|
||
// print("serviece $service");
|
||
for (BluetoothCharacteristic element in service.characteristics) {
|
||
if (isNotify == false && element.properties.notify) {
|
||
await element.setNotifyValue(true);
|
||
print("setNotifyValue 完成");
|
||
connectedDeviceProp.createLisetenReceive(element);
|
||
isNotify = true;
|
||
if (connectedDeviceProp.connectedDevicePropType ==
|
||
ConnectedDevicePropType.JunHe) {
|
||
continue;
|
||
}
|
||
if (connectedDeviceProp.connectedDevicePropType ==
|
||
ConnectedDevicePropType.MHT) {
|
||
continue;
|
||
}
|
||
}
|
||
if (isWrite == false && element.properties.write) {
|
||
connectedDeviceProp?.writeCharacteristic = element;
|
||
isWrite = true;
|
||
print("$element");
|
||
}
|
||
}
|
||
}
|
||
if (!isWrite || !isNotify) {
|
||
if (connectedDeviceProp != null) {
|
||
disconnect(connectedDeviceProp!);
|
||
}
|
||
print("service 订阅失败 isWrite $isWrite isNotify $isNotify");
|
||
fun['fail']?.call("service 订阅失败 isWrite $isWrite isNotify $isNotify");
|
||
return;
|
||
}
|
||
print("service 注册完成");
|
||
connectList[connectedDeviceProp.id] = connectedDeviceProp;
|
||
connectedDeviceProp.createListenState();
|
||
if (connectedDeviceProp.connectedDevicePropType ==
|
||
ConnectedDevicePropType.JunHe) {
|
||
connectedDeviceProp.heartbeat();
|
||
}
|
||
print("回调成功");
|
||
fun['success']?.call(connectedDeviceProp);
|
||
} catch (e) {
|
||
print("连接失败 执行失败回调 错误: $e");
|
||
if (connectedDeviceProp != null) {
|
||
disconnect(connectedDeviceProp!);
|
||
}
|
||
print("连接失败 执行失败回调");
|
||
fun['fail']?.call(e);
|
||
}
|
||
}
|
||
|
||
// 连接设备
|
||
void connectToDevice(fun) async {
|
||
if (fun != null) {
|
||
return;
|
||
}
|
||
BluetoothDevice device = fun.device;
|
||
ConnectedDeviceProp? connectedDeviceProp =
|
||
getOneConnectedDeviceProp(device.remoteId.str);
|
||
if (connectedDeviceProp != null) {
|
||
disconnect(connectedDeviceProp);
|
||
Future.delayed(const Duration(seconds: 1), () {
|
||
connectToDevice(fun);
|
||
});
|
||
return;
|
||
}
|
||
try {
|
||
print("connecting");
|
||
await device.connect(timeout: const Duration(seconds: 8));
|
||
print("device.connect success");
|
||
ConnectedDevicePropType connectedDevicePropType =
|
||
ConnectedDevicePropType.JunHe;
|
||
connectedDeviceProp = ConnectedDeviceProp(
|
||
connectDevice: device,
|
||
fun: fun,
|
||
connectedDevicePropType: connectedDevicePropType);
|
||
if (Platform.isAndroid) {
|
||
await device.requestMtu(mcuMax);
|
||
}
|
||
Timer(const Duration(milliseconds: 1000), () {
|
||
setOther(device, connectedDeviceProp, fun);
|
||
});
|
||
} catch (e) {
|
||
print("连接失败 执行失败回调 错误: $e");
|
||
if (connectedDeviceProp != null) {
|
||
disconnect(connectedDeviceProp!);
|
||
}
|
||
print("连接失败 执行失败回调");
|
||
fun['fail']?.call(e);
|
||
}
|
||
}
|
||
|
||
bool jsJunHe(String name) {
|
||
return "$name".contains("AITH-V2") || "$name".contains("AITH-V2") || "$name".contains("AITH-V2");
|
||
}
|
||
|
||
|
||
void disconnect(ConnectedDeviceProp connectedDeviceProp) {
|
||
connectedDeviceProp.closeHeartBeat();
|
||
connectList.remove(connectedDeviceProp.id);
|
||
connectedDeviceProp.closeConnectedDeviceProp();
|
||
}
|
||
|
||
void closeAll() {
|
||
findCall = null;
|
||
connectList.values.toList().forEach((element) {
|
||
disconnect(element);
|
||
});
|
||
}
|
||
|
||
enum ConnectedDevicePropType { JunHe, QuanShi, MHT }
|
||
|
||
class ConnectedDeviceProp {
|
||
ConnectedDevicePropType connectedDevicePropType;
|
||
Timer? heartbeatTimer = null;
|
||
int _seq = 0;
|
||
var connectDevice;
|
||
var writeCharacteristic;
|
||
var listenState;
|
||
StreamSubscription<List<int>>? lisetenReceive;
|
||
Map fun;
|
||
List receiveMethods = [];
|
||
List logList = [];
|
||
Function? logChange;
|
||
ConnectedDeviceProp(
|
||
{required this.connectDevice,
|
||
required Map this.fun,
|
||
this.connectedDevicePropType = ConnectedDevicePropType.JunHe});
|
||
List receiveLogArr = [];
|
||
int deviceType = 2;
|
||
int encodeType = 2;
|
||
List sendArr = [];
|
||
double sendExecAverage = 100;
|
||
bool isClose = false;
|
||
|
||
String get id {
|
||
return connectDevice.remoteId.str;
|
||
}
|
||
|
||
int sum_ab(dv) {
|
||
ByteData sum = ByteData(1);
|
||
for (int i = 0; i < dv.buffer.lengthInBytes; i++) {
|
||
sum.setUint8(0, dv.getUint8(i) + sum.getUint8(0));
|
||
}
|
||
return sum.getUint8(0);
|
||
}
|
||
|
||
void heartbeat() {
|
||
closeHeartBeat();
|
||
heartbeatTimer = Timer.periodic(const Duration(seconds: 8), (timer) {
|
||
ByteData dv = ByteData(4);
|
||
dv.setUint8(0, 4);
|
||
dv.setUint8(2, seq);
|
||
dv.setUint8(3, 5);
|
||
dv.setUint8(1, sum_ab(dv));
|
||
writeBle(dv);
|
||
});
|
||
}
|
||
|
||
closeHeartBeat() {
|
||
if (heartbeatTimer != null) {
|
||
heartbeatTimer!.cancel();
|
||
heartbeatTimer = null;
|
||
}
|
||
}
|
||
|
||
ByteData str2ab_oneByte(String str, {int startLength = 0}) {
|
||
Uint8List utf8str = utf8.encode(str);
|
||
int len = utf8str.length + startLength;
|
||
ByteData buf2 = ByteData.sublistView(utf8str);
|
||
ByteData buf = ByteData(len);
|
||
for (int i = startLength; i < len; i++) {
|
||
buf.setUint8(i, buf2.getUint8(i - startLength));
|
||
}
|
||
return buf;
|
||
}
|
||
|
||
void write3OfString(sendDate, {Function? success, Function? fail}) {
|
||
ByteData dv = str2ab_oneByte(sendDate, startLength: 4);
|
||
int len = dv.buffer.lengthInBytes;
|
||
dv.setUint8(0, len);
|
||
dv.setUint8(2, seq);
|
||
dv.setUint8(3, 8 * 16 + 3);
|
||
dv.setUint8(1, sum_ab(dv));
|
||
writeBle(dv, success: success, fail: fail);
|
||
}
|
||
|
||
void writeBle(ByteData d, {Function? success, Function? fail}) {
|
||
Uint8List d_ = Uint8List.view(d.buffer);
|
||
if (sendArr.length == 0) {
|
||
write(d_, success, fail);
|
||
}
|
||
sendArr.insert(0, {"d": d_, "success": success, "fail": fail});
|
||
}
|
||
|
||
void write(Uint8List d, Function? success, Function? fail, {int exec = 100}) {
|
||
if (writeCharacteristic != null) {
|
||
// try {
|
||
// if (d[3] == 8 * 16 + 3) {
|
||
// print(
|
||
// "blewrite s = $sendExecAverage d = ${utf8.decode(d.sublist(4))}");
|
||
// } else {
|
||
// print("ble last write d = ${d[3]}");
|
||
// }
|
||
// } catch (e) {
|
||
// print("write logprint error $e");
|
||
// }
|
||
writeCharacteristic.write(d, withoutResponse: true).then((e) {
|
||
// print("write success $e");
|
||
if (connectedDevicePropType != ConnectedDevicePropType.JunHe) {
|
||
print("发送 $d");
|
||
}
|
||
if (exec > 95) {
|
||
sendExecAverage = sendExecAverage + 0.5;
|
||
}
|
||
if (sendExecAverage > 99) {
|
||
sendExecAverage = 99;
|
||
}
|
||
if (sendArr.length > 0) {
|
||
sendArr.removeLast();
|
||
Map last = sendArr.last;
|
||
write(last["d"], last["success"], last["fail"]);
|
||
}
|
||
success?.call();
|
||
}).catchError((e) {
|
||
// print("exec = $exec , $e");
|
||
if (exec < 0) {
|
||
print("$e");
|
||
fail?.call();
|
||
}
|
||
if (exec > -1 && isClose == false) {
|
||
int time = ((100.0 - sendExecAverage) * 5.0).toInt();
|
||
if (exec < 80) {
|
||
time = (100 - exec) * 5;
|
||
sendExecAverage = exec * 1.0;
|
||
} else {
|
||
sendExecAverage = sendExecAverage - (100 - exec) * 0.1;
|
||
}
|
||
Timer(Duration(milliseconds: time), () {
|
||
write(d, success, fail, exec: exec - 1);
|
||
});
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
void read6() {
|
||
ByteData dv = ByteData(4);
|
||
dv.setUint8(0, 4);
|
||
dv.setUint8(2, seq);
|
||
dv.setUint8(3, 6);
|
||
dv.setUint8(1, sum_ab(dv));
|
||
writeBle(dv);
|
||
}
|
||
|
||
addLog(String log) {
|
||
if (logList.length > 500) {
|
||
logList.removeRange(0, 50);
|
||
}
|
||
DateTime date = DateTime.now();
|
||
String h = date.hour > 10 ? "${date.hour}" : "0${date.hour}";
|
||
String m = date.minute > 10 ? "${date.minute}" : "0${date.minute}";
|
||
String s = date.second > 10 ? "${date.second}" : "0${date.second}";
|
||
logList.add({"time": "$h:$m:$s", "value": log});
|
||
print("ble $id log: $log");
|
||
if (logChange != null) {
|
||
logChange?.call(logList, log);
|
||
}
|
||
}
|
||
|
||
createListenState() {
|
||
listenState = connectDevice.connectionState.listen((state) {
|
||
print('ble Device state $id $state');
|
||
if (state == BluetoothConnectionState.disconnected) {
|
||
print('ble Device state $id disconnected');
|
||
isClose = true;
|
||
disconnect(this);
|
||
fun['stateChange']?.call(state, this);
|
||
}
|
||
});
|
||
}
|
||
|
||
createLisetenReceive(BluetoothCharacteristic element) {
|
||
lisetenReceive = element.onValueReceived.listen((List<int> value) {
|
||
if (connectedDevicePropType == ConnectedDevicePropType.JunHe) {
|
||
if (value.isEmpty) {
|
||
return;
|
||
}
|
||
bool isOk = sumCheck(value);
|
||
if (isOk) {
|
||
receiveMethods.forEach((m) {
|
||
m?.call();
|
||
});
|
||
yewuSwitch(value[3], value.sublist(4));
|
||
}
|
||
} else {
|
||
print("onValueReceived $value");
|
||
receiveLogArr.forEach((m) {
|
||
m(value);
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
closeConnectedDeviceProp() {
|
||
isClose = true;
|
||
if (listenState != null) {
|
||
listenState?.cancel();
|
||
}
|
||
if (lisetenReceive != null) {
|
||
lisetenReceive?.cancel();
|
||
}
|
||
connectDevice?.disconnect();
|
||
}
|
||
|
||
int get seq {
|
||
int r = _seq % 256;
|
||
_seq++;
|
||
return r;
|
||
}
|
||
|
||
bool sumCheck(List<int> ab) {
|
||
ByteData dv = ByteData.sublistView(Uint8List.fromList(ab));
|
||
|
||
if (dv.getUint8(0) != ab.length) {
|
||
print("和校验失败:长度不对");
|
||
return false;
|
||
} //和校验失败
|
||
|
||
if (sumList(ab) != dv.getUint8(1)) {
|
||
print("和校验失败: 校验失败");
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
int sumList(List<int> ab) {
|
||
ByteData dv = ByteData.sublistView(Uint8List.fromList(ab));
|
||
ByteData sum = ByteData.sublistView(Uint8List(1));
|
||
|
||
sum.setUint8(0, dv.getUint8(0));
|
||
|
||
for (int i = 2; i < ab.length; i++) {
|
||
sum.setUint8(0, dv.getUint8(i) + sum.getUint8(0));
|
||
}
|
||
|
||
return sum.getUint8(0);
|
||
}
|
||
|
||
List<int> endLogValue = [];
|
||
Timer? endLogTimer;
|
||
|
||
void yewuSwitch(int yewu, List<int> abData) {
|
||
switch (yewu) {
|
||
case 7:
|
||
String error = ab2StrByType(abData);
|
||
print(error);
|
||
break;
|
||
|
||
case 131:
|
||
List<int>? logData;
|
||
if (abData.last != 10) {
|
||
int index = abData.lastIndexOf(10);
|
||
if (index == -1) {
|
||
index = abData.length;
|
||
endLogValue = [...endLogValue, ...abData.sublist(0, index)];
|
||
} else {
|
||
logData = [...endLogValue, ...abData.sublist(0, index)];
|
||
endLogValue = abData.sublist(index);
|
||
}
|
||
// if (index == -1) {
|
||
// index = abData.length;
|
||
// }
|
||
// if(endLogValue.isNotEmpty) {
|
||
// logData = [...endLogValue, ...abData.sublist(0, index)];
|
||
// endLogValue = [];
|
||
// } else {
|
||
// logData = [...abData.sublist(0, index)];
|
||
// }
|
||
// if(index != abData.length) {
|
||
// endLogValue = abData.sublist(index);
|
||
// }
|
||
} else {
|
||
int index = abData.length;
|
||
|
||
if (endLogValue.isNotEmpty) {
|
||
logData = [...endLogValue, ...abData];
|
||
} else {
|
||
logData = abData;
|
||
}
|
||
|
||
endLogValue = [];
|
||
}
|
||
|
||
if (endLogTimer != null) {
|
||
endLogTimer!.cancel();
|
||
endLogTimer = null;
|
||
}
|
||
|
||
if (endLogValue != null && endLogValue!.isNotEmpty) {
|
||
endLogTimer = Timer(Duration(milliseconds: 400), () {
|
||
String log = ab2StrByType(endLogValue!);
|
||
endLogValue = [];
|
||
addLog(log);
|
||
try {
|
||
receiveLogArr.forEach((m) {
|
||
m(log);
|
||
});
|
||
} catch (e) {
|
||
print(e);
|
||
}
|
||
});
|
||
}
|
||
|
||
if (logData != null && logData.isNotEmpty) {
|
||
if (logData.length != 1 || logData[0] != 13) {
|
||
String log = ab2StrByType(logData!);
|
||
addLog(log);
|
||
try {
|
||
receiveLogArr.forEach((m) {
|
||
m(log);
|
||
});
|
||
} catch (e) {
|
||
print(e);
|
||
}
|
||
}
|
||
}
|
||
// 处理逻辑
|
||
break;
|
||
|
||
case 132:
|
||
ByteData dv = ByteData.sublistView(Uint8List.fromList(abData));
|
||
|
||
for (int i = 0; i < abData.length;) {
|
||
int len = dv.getUint8(i);
|
||
yewuSwitch(dv.getUint8(i + 1), abData.sublist(i + 2, i + 1 + len));
|
||
i = i + 1 + len;
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
String ab2StrByType(List<int> abData) {
|
||
// Implement your logic for converting abData to String
|
||
String str = "";
|
||
if (abData.isNotEmpty) {
|
||
try {
|
||
str = utf8.decode(abData);
|
||
} catch (e) {
|
||
str = "解析错误";
|
||
}
|
||
}
|
||
return str;
|
||
}
|
||
}
|