import 'dart:async'; import 'dart:convert'; import 'dart:core'; import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; import 'package:broadcast/broadcast.dart'; import 'package:buffer/buffer.dart'; import 'package:device_info/device_info.dart'; import 'package:flutter/material.dart'; import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; import 'package:get_it/get_it.dart'; import 'package:package_info/package_info.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:sport/application.dart'; import 'package:sport/bean/hardware.dart'; import 'package:sport/db/bluetooth_db.dart'; import 'package:sport/db/step_db.dart'; import 'package:sport/game/sdk_parse.dart'; import 'package:sport/pages/my/dfu_update_page.dart'; import 'package:sport/pages/web/game_log.dart'; import 'package:sport/services/Converter.dart'; import 'package:sport/services/api/inject_api.dart'; import 'package:sport/services/api/rest_client.dart'; import 'package:sport/utils/toast.dart'; import 'package:sport/utils/version.dart'; import 'package:sport/widgets/dialog/alert_dialog.dart'; const BLE_CODE_0 = 0x00; const BLE_CODE_1 = 0x01; class BLE { BLE._(); static const BLE_Client_T_MOTION = 0x04; static const BLE_Client_T_UPDATE = 0xA1; static const BLE_Client_T_GAMEMODE = 0xA2; static const BLE_Client_T_CONNET_R = 0xA3; static const BLE_Client_T_SHOCK = 0xA4; static const BLE_Client_T_REALTIMESTEP = 0xA5; static const BLE_Client_T_DFU = 0xA6; static const BLE_Client_T_CHARGE = 0xA7; static const BLE_Client_T_LIGHTING = 0xAC; static const BLE_Client_T_ERR = 0xAD; static const BLE_Client_T_CONNECT_CONFIRM = 0xB0; } class BLE_UPDATE { BLE_UPDATE._(); static const BASIC = 0x00; static const DATA = 0x01; static const STEP = 0x02; static const STEP_DELETE = 0x03; } const BLE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"; class Bluetooth with InjectApi, GameLog { static final String PREF_KEY = "SHOES"; static final String PREF_KEY_NAME = "SHOES_NAME"; factory Bluetooth() => _getInstance(); static Bluetooth? _instance; // 获取对象 static Bluetooth _getInstance() { if (_instance == null) { // 使用私有的构造方法来创建对象 _instance = Bluetooth._internal(); } return _instance!; } Bluetooth._internal() { //初始化(设置一些默认的)... } final List> _streamSubscriptions = >[]; void addSubscription(StreamSubscription streamSubscription) { _streamSubscriptions.add(streamSubscription); } disposeSubscription() async { timerConnected?.cancel(); characteristicWrite = null; characteristicNotify = null; if(_streamSubscriptions.isNotEmpty) { try { for (StreamSubscription subscription in _streamSubscriptions) { await subscription.cancel(); } } catch (e) { print(e); } } _streamSubscriptions.clear(); } // ignore: close_sinks final StreamController _queryController = StreamController.broadcast(); Stream get queryStream => _queryController.stream; // ignore: close_sinks final StreamController _dataController = StreamController.broadcast(); Stream get dataStream => _dataController.stream; // ignore: close_sinks final StreamController _connectRightController = StreamController.broadcast(); Stream get connectRightStream => _connectRightController.stream; // ignore: close_sinks final StreamController _dfuController = StreamController.broadcast(); Stream get dfuStream => _dfuController.stream; // ignore: close_sinks final StreamController _sdkCmdController = StreamController.broadcast(); Stream get sdkCmdStream => _sdkCmdController.stream; // ignore: close_sinks final StreamController> _sdkMotionController = StreamController.broadcast(); Stream> get sdkMotionStream => _sdkMotionController.stream; // ignore: close_sinks final StreamController _sdkMotionDataController = StreamController.broadcast(); Stream get sdkMotionDataStream => _sdkMotionDataController.stream; final ValueNotifier deviceNotifier = ValueNotifier(null); final ValueNotifier deviceNameNotifier = ValueNotifier(""); final ValueNotifier infoNotifier = ValueNotifier({}); final ValueNotifier versionNotifier = ValueNotifier(""); final ValueNotifier dataNotifier = ValueNotifier({}); final ValueNotifier electricityNotifier = ValueNotifier(0); final ValueNotifier stepNotifier = ValueNotifier(0); final ValueNotifier stepTotalNotifier = ValueNotifier(0); final ValueNotifier stepTotalTestNotifier = ValueNotifier(0); final ValueNotifier actionNotifier = ValueNotifier(0); final ValueNotifier> byteNotifier = ValueNotifier>([]); final ValueNotifier vibrateNotifier = ValueNotifier(400); final ValueNotifier> stepRealtimeNotifier = ValueNotifier>([]); final ValueNotifier stepRealtimePageNotifier = ValueNotifier(false); final ValueNotifier verNotifier = ValueNotifier(null); final ValueNotifier> stateNotifier = ValueNotifier({1: 0, 0: 0}); final ValueNotifier h5gameRNotifier = ValueNotifier(false); final ValueNotifier deviceWriteNotifier = ValueNotifier(false); final ValueNotifier deviceReadNotifier = ValueNotifier(false); final ValueNotifier deviceReadNotifyNotifier = ValueNotifier(false); final ValueNotifier rssiNotifier = ValueNotifier(0); final ValueNotifier> timeUseNotifier = ValueNotifier>({}); DiscoveredDevice? _connectingDevice; bool _listen = false; bool _online = false; bool _uploadLoged = false; bool _connecting = false; bool _scanning = false; String? _deviceId; int heartbeat = 0; PartInfo? _partInfo; Completer? _completerStep; Completer? _completerStepDel; Completer? _completerSetupDeviceVer; Completer? _completerConnected; QualifiedCharacteristic? characteristicWrite, characteristicNotify; StreamSubscription? statusStream, connectRight; late FlutterReactiveBle flutterReactiveBle; Timer? stepRealTimeTimer; Timer? gameModeTimer; Timer? timerConnected; Timer? rssiTimer; Timer? disconnectTimer; Timer? tryConnectTimer; int disconnectTimes = 0; DateTime? startTime; List timeUse = []; int testDfu = 0; int _isRightError = 0; bool get isConnected => _online; bool get isNotReady => isDebugShoe ? false : !(_online == true && electricityNotifier.value > 0); bool get isRightError => _isRightError != 1; String? get deviceId => _deviceId; DiscoveredDevice? get device => _connectingDevice; late BuildContext _context; bool _showChargeDialog = false; DateTime? electricityTime; late SDKApi sdk; resetData() { stepNotifier.value = 0; stepTotalNotifier.value = 0; actionNotifier.value = 0; } bool _dfu = false; set dfu(bool dfu) { _dfu = dfu; } Future _saveDevice(DiscoveredDevice device) async { var prefs = await SharedPreferences.getInstance(); prefs.setString(PREF_KEY, device.id); prefs.setString(PREF_KEY_NAME, device.name); return device.id; } clearDevice({bool deleteStep = false}) async { var prefs = await SharedPreferences.getInstance(); prefs.remove(PREF_KEY); prefs.remove(PREF_KEY_NAME); // if (deleteStep == true) { // var db = StepDB(); // db.deleteAll(); // } startTime = null; await disconnectDevice("清空设备", clearGatt: true, click: true); } Future getHistoryDevice() async { var prefs = await SharedPreferences.getInstance(); return prefs.getString(PREF_KEY); } init(BuildContext context) async { this._connecting = false; statusStream?.cancel(); connectRight?.cancel(); flutterReactiveBle = FlutterReactiveBle(); flutterReactiveBle.logLevel = inProduction ? LogLevel.none : LogLevel.verbose; statusStream = flutterReactiveBle.statusStream.listen((state) { print("bluetooth -- instance state $state"); if (state == BleStatus.ready) { autoConnect(); } else if (state == BleStatus.unknown) { } else if (state == BleStatus.poweredOff) { // clearDevice(deleteStep: false); // ToastUtil.show("蓝牙服务已关闭"); } else {} }); // flutterReactiveBle.connectedDeviceStream.listen((event) { // print("bluetooth -- device state $event"); // if (event.connectionState == DeviceConnectionState.disconnected) { // _online = false; // tryCount--; // if (event.failure?.code == ConnectionError.failedToConnect) { // notifyConnectTime("[$tryCount]E${RegExp(r"([0-9]+)").firstMatch("${event.failure?.message}")?.group(0)}"); // // addErr("0", "${event.failure?.code}", "${event.failure?.message}"); // } else { // notifyConnectTime("[$tryCount]E0"); // } // disposeSubscription(); // // if (event.failure == null) { // try { // _completerConnected?.complete(true); // } catch (e) {} // } // } // }); connectRight = connectRightStream.listen((event) { print("bluetooth -- connect right ${_connectingDevice?.name} ${event}!"); if (event == 1 && _isRightError != event) { notifyConnectTime("R"); timerConnected?.cancel(); dataNotifier.value = {}; verNotifier.value = null; // 左右鞋连接状态就绪,正常连接 final device = _connectingDevice; if (device != null) { _newConnect(checkDfuUpdate: false); _connected(device); _query(); } } _isRightError = event; }); deviceNotifier.addListener(() { if (deviceNotifier.value != null) { DiscoveredDevice device = deviceNotifier.value!; deviceNameNotifier.value = device.name; BluetoothDB().find().then((history) { if (history.isNotEmpty == true) { try { var item = history.firstWhere((element) => element[BluetoothDB.C_ID] == device.id); deviceNameNotifier.value = item[BluetoothDB.C_MARK] ?? device.name ?? ""; } catch (e) {} } }); } else { deviceNameNotifier.value = ""; } }); Broadcast.eventStream.listen((event) { disconnectDevice("iOS 进入游戏"); }); sdk = SDKApi(); this.listen(context, init: true); } gameInit(int gameType) { sdk.gameInit(gameType); } SDKApi gameSDK() { return sdk; } createGameLog(String? name) async { super.createLog("${name}_${sdk.getVersion()}", device?.name ?? deviceId); } uploadGameLog() { uploadLog(); } _newConnect({bool checkDfuUpdate = true}) { final device = _connectingDevice; if (device != null) { _saveDevice(device); // if (isDebugShoe) { // ToastUtil.show("${device.name} 已连接"); // } BluetoothDB().insert(device); if (checkDfuUpdate == true) checkUpdate(); } } Future _hasConnected() async { final device = _connectingDevice; if (device != null) { return (await BluetoothDB().findById(device.id)).isNotEmpty; } return false; } Future listen(BuildContext context, {bool init = false}) async { this._context = context; this._listen = true; print("bluetooth -- listen"); Broadcast.broadcast("SHOE.SDK.BLUE_DISCONNECT"); var prefs = await SharedPreferences.getInstance(); if (!prefs.containsKey("token")) { return; } disconnectTimer?.cancel(); disconnectTimes = 0; if (_dfu == true) return; if (init != true) { if (isConnected) { setupGameMode4h5(h5gameRNotifier.value); } else { autoConnect(); } } } void runTimer() { heartbeat = 0; addSubscription(Stream.periodic(Duration(seconds: 60)).listen((event) async { if (!isConnected) return; heartbeat++; if (heartbeat > 3) { disconnectDevice("心跳异常"); return; } await queryDeviceData(); await queryDeviceStep(); })); } autoConnect() { getHistoryDevice().then((value) { if (this._connecting == true) return; if (value != null) connectDevice(id: value); }); } Future background(BuildContext context) async { this._listen = false; if (_showChargeDialog == true) { Navigator.maybePop(context); } disconnectTimer?.cancel(); disconnectTimer = Timer.periodic(Duration(seconds: 10), (t) { disconnectTimes++; if (disconnectTimes >= 720) { disconnectDevice("后台时断开连接"); t.cancel(); } }); } Future disposeBluetooth(BuildContext context) async { if (_dfu == true) return; // // 实时计步中不主动断开 if (stepRealtimePageNotifier.value == true) return; gameModeTimer?.cancel(); this._listen = false; if (_showChargeDialog == true) { Navigator.maybePop(context); } // if (Platform.isIOS) { await disconnectDevice("app退后台"); // } } resetNotifierData() { dataNotifier.value = {}; verNotifier.value = null; versionNotifier.value = ""; electricityNotifier.value = -1; _connectRightController.add(0); stateNotifier.value = {0: 0, 1: 0}; notifyConnectTime("dis"); deviceWriteNotifier.value = false; deviceReadNotifier.value = false; deviceReadNotifyNotifier.value = false; _online = false; } Future disconnectDevice(String msg, {bool clearGatt = false, bool click = false}) async { print("bluetooth -- disconnect $msg ${_connectingDevice?.id}"); _scanning = false; _deviceId = null; await disposeSubscription(); try { _completerConnected?.complete(true); } catch (e) {} if (click == true) Broadcast.broadcast("SHOE.SDK.BLUE_DISCONNECT"); resetNotifierData(); _isRightError = 0; disconnectTimes = 0; _uploadLoged = false; _connectingDevice = null; deviceNotifier.value = null; errQueue.clear(); } connect({DiscoveredDevice? device}) async { await connectDevice(discoveredDevice: device); } Future connectDevice({DiscoveredDevice? discoveredDevice, String? id, bool again = false}) async { await disconnectDevice("主动连接"); this._connecting = true; // await _connectDeviceInner(discoveredDevice: discoveredDevice, id: id, again: again); timeUse = []; timeUseNotifier.value = {}; if (discoveredDevice != null) { startTime = null; } notifyConnectTime("F"); DiscoveredDevice? device = discoveredDevice; if (device == null) { device = await discoverDevice(deviceId: id); if (device == null) { await disconnectDevice("没有找到设备"); return; } notifyConnectTime("FF"); // 1秒可能会提高连接成功率 await Future.delayed(Duration(seconds: 1)); } _connectingDevice = device; await _connectDeviceInner(device); } Future _connectDeviceInner(DiscoveredDevice device) async { int tryCount = 3; while (tryCount > 0) { print("bluetooth -- connect id: ${device.id} (test=$tryCount) !"); if (!_connecting) break; if (device.id != _connectingDevice?.id) break; String deviceId = _deviceId = device.id; try { if (Platform.isIOS && tryCount != 3) { DiscoveredDevice? device = await discoverDevice(deviceId: deviceId); if (device == null) { tryCount--; continue; } deviceId = device.id; notifyConnectTime("FF"); // 1秒可能会提高连接成功率 await Future.delayed(Duration(seconds: 1)); } addSubscription(flutterReactiveBle.connectToDevice(id: deviceId, connectionTimeout: const Duration(seconds: 10)).listen((state) async { print("bluetooth -- connect state $state $_connecting "); if (state.connectionState == DeviceConnectionState.connected) { notifyConnectTime("[$tryCount]C"); // bindConnectedDevice(id: deviceId); int result = await discoverServices(flutterReactiveBle, deviceId); notifyConnectTime("[$tryCount]D$result"); print("bluetooth -- connect discover services $result"); if (result != 3) { try { _completerConnected?.complete(true); } catch (e) {} return; } if (characteristicNotify != null) { print("bluetooth -- subscribe notify $characteristicNotify"); addSubscription(flutterReactiveBle.subscribeToCharacteristic(characteristicNotify!).listen((event) { // print("bluetooth -- subscribe notify $event"); deviceReadNotifyNotifier.value = true; byteNotifier.value = event; heartbeat = 0; parse(event); }) ..onError((e) { print("bluetooth -- subscribe notify e -> $e"); disconnectDevice("订阅服务失败"); })); } Stream.periodic(const Duration(seconds: 1)).take(10).forEach((element) { write(BLE.BLE_Client_T_CONNECT_CONFIRM, Uint8List.fromList([BLE_CODE_1]), test: 10); }); int timeout = 10; timerConnected = Timer.periodic(Duration(seconds: 1), (t) { queryDeviceInfo(); queryDeviceRight(); if (--timeout < 0) { if (isRightError) { clearDevice(); } t.cancel(); } }); tryCount = 3; } else if (state.connectionState == DeviceConnectionState.disconnected) { tryCount--; notifyConnectTime("[$tryCount]E${RegExp(r"([0-9]+)").firstMatch("${state.failure?.message}")?.group(0) ?? "0"}"); try { _completerConnected?.complete(!(state.failure?.code == ConnectionError.failedToConnect)); } catch (e) {} } }) ..onError((e) {})); if (_connecting) { var result = await (_completerConnected = Completer()).future; // true是主动断开 _connecting = result != true; await disposeSubscription(); if (!_connecting) break; } } catch (e) { print("bluetooth -- connect error: $e"); print(e); break; } await Future.delayed(Duration(seconds: 3)); } if(_connecting) { await disconnectDevice("断开蓝牙连接 $_connecting"); } // // notifyConnectTime("F"); // // if (discoveredDevice == null) { // DiscoveredDevice? d = await discoverDevice(deviceId: id); // if (d != null) { // discoveredDevice = d; // notifyConnectTime("FF"); // } else { // disconnectDevice("搜索不到设备", clearGatt: true); // return; // } // } // // String deviceId = _deviceId = discoveredDevice.id; // _connectingDevice = discoveredDevice; // // print("bluetooth -- connect $deviceId $again!"); // // final stream = flutterReactiveBle.connectToDevice(id: deviceId, connectionTimeout: const Duration(seconds: 10)); // addSubscription(stream.listen(_connectState, onError: (Object e) { // disconnectDevice("连接服务异常", clearGatt: true); // })); } Future discoverDevice({String? deviceId, String? deviceName}) async { // 2022-04-26 11:10:53.285 17454-17479/com.ouj.sdk.test I/System.out: 00001800-0000-1000-8000-00805f9b34fb // 2022-04-26 11:10:53.285 17454-17479/com.ouj.sdk.test I/System.out: 00002a00-0000-1000-8000-00805f9b34fb // 2022-04-26 11:10:53.285 17454-17479/com.ouj.sdk.test I/System.out: 00002a01-0000-1000-8000-00805f9b34fb // 2022-04-26 11:10:53.285 17454-17479/com.ouj.sdk.test I/System.out: 00002a04-0000-1000-8000-00805f9b34fb // 2022-04-26 11:10:53.285 17454-17479/com.ouj.sdk.test I/System.out: 00002aa6-0000-1000-8000-00805f9b34fb // 2022-04-26 11:10:53.285 17454-17479/com.ouj.sdk.test I/System.out: 00001801-0000-1000-8000-00805f9b34fb // 2022-04-26 11:10:53.285 17454-17479/com.ouj.sdk.test I/System.out: 6e400001-b5a3-f393-e0a9-e50e24dcca9e // 2022-04-26 11:10:53.285 17454-17479/com.ouj.sdk.test I/System.out: 6e400002-b5a3-f393-e0a9-e50e24dcca9e // 2022-04-26 11:10:53.285 17454-17479/com.ouj.sdk.test D/BluetoothController: discovered characteristic write android.bluetooth.BluetoothGatt@28bbf97 android.bluetooth.BluetoothGattCharacteristic@81cd69e // 2022-04-26 11:10:53.285 17454-17479/com.ouj.sdk.test I/System.out: 6e400003-b5a3-f393-e0a9-e50e24dcca9e // 2022-04-26 11:10:53.285 17454-17479/com.ouj.sdk.test D/BluetoothGatt: setCharacteristicNotification() - uuid: 6e400003-b5a3-f393-e0a9-e50e24dcca9e enable: true // 2022-04-26 11:10:53.286 17454-17479/com.ouj.sdk.test D/BluetoothController: discovered characteristic notify android.bluetooth.BluetoothGatt@28bbf97 android.bluetooth.BluetoothGattCharacteristic@9dfb57f // 2022-04-26 11:10:53.286 17454-17479/com.ouj.sdk.test I/System.out: 0000fe59-0000-1000-8000-00805f9b34fb // 2022-04-26 11:10:53.286 17454-17479/com.ouj.sdk.test I/System.out: 8ec90003-f315-4f60-9fb8-838830daea50 _scanning = true; Timer timeout = Timer(Duration(seconds: 10), () { _scanning = false; }); var stream = flutterReactiveBle.scanForDevices(withServices: [Uuid.parse(BLE_UUID)], scanMode: ScanMode.lowPower); DiscoveredDevice? device; await for (var event in stream) { print('bluetooth -- scan device : ${event.id} ${event.name}'); if (_scanning != true) { break; } if (event.id == deviceId || event.name == deviceName || (deviceName != null && event.name.contains(deviceName))) { device = event; break; } } timeout.cancel(); print('bluetooth -- found: ${device?.id} ${device?.name}'); return device; } Future discoverServices(FlutterReactiveBle flutterReactiveBle, String deviceId) async { int result = 0; for (var i = 0; i < 3; i++) { bool findWrite = false; bool findRead = false; try { var services = await flutterReactiveBle.discoverServices(deviceId); for (DiscoveredService service in services) { for (var characteristic in service.characteristics) { if (characteristic.isWritableWithoutResponse) { characteristicWrite = QualifiedCharacteristic(serviceId: service.serviceId, characteristicId: characteristic.characteristicId, deviceId: deviceId); findWrite = true; } if (characteristic.isNotifiable) { characteristicNotify = QualifiedCharacteristic(serviceId: service.serviceId, characteristicId: characteristic.characteristicId, deviceId: deviceId); findRead = true; } } } } catch (e) { print(e); print("bluetooth -- service error $e"); } deviceWriteNotifier.value = findWrite; deviceReadNotifier.value = findRead; if (findWrite && findRead) { print("bluetooth -- service write: $findWrite, read: $findRead"); result = 3; break; } else { await Future.delayed(Duration(seconds: 2)); if (findWrite) { result = 1; } else if (findRead) { result = 2; } } } return result; } _query() async { queryDeviceInfo(); queryDeviceStep(); queryDeviceCharge(); queryDeviceData(); } _connected(DiscoveredDevice newDevice) async { print("bluetooth -- connected: $newDevice"); deviceNotifier.value = newDevice; _dfuController.add(false); _online = true; _partInfo = null; if ((startTime?.difference(DateTime.now()).inHours ?? 1) > 0) { notifyConnected(); startTime = DateTime.now(); } setupGameMode4h5(h5gameRNotifier.value); runTimer(); } notifyConnectTime(String tag) { int now = DateTime.now().millisecondsSinceEpoch; Map result = Map.from(timeUseNotifier.value); // print("111111111111111111 ${now} ${timeUse}"); result[tag] = now - (timeUse.isNotEmpty ? timeUse.last : now); timeUseNotifier.value = result; timeUse.add(now); } notifyConnected() { this.vibrate(200); this.lighting(10000); } checkUpdate() async { var info = infoNotifier.value; print("bluetooth -- checkUpdate ${info}"); if (info.isEmpty == true) return; if (h5gameRNotifier.value == true) return; String softwareVer = info['softwareVer']; String hardwareVer = info['hardwareVer']; List device = info['device']; PackageInfo packageInfo = await PackageInfo.fromPlatform(); String appVer = packageInfo.version; Hardware? updateInfo = (await GetIt.I().checkHardwareUpdate(hardwareVer, appVer)).data; if (updateInfo != null) { // PackageInfo packageInfo = await PackageInfo.fromPlatform(); // if (updateInfo.min_app_ver?.isNotEmpty == true && versionCompare(packageInfo.version, updateInfo.min_app_ver ?? "0") < 0) { // debugPrint("bluetooth -- dfu update app ver ${packageInfo.version} min ${updateInfo.min_app_ver}"); // return; // } // if (updateInfo.max_app_ver?.isNotEmpty == true && versionCompare(packageInfo.version, updateInfo.max_app_ver ?? "0") > 0) { // debugPrint("bluetooth -- dfu update app ver ${packageInfo.version} max ${updateInfo.max_app_ver}"); // return; // } // // if (updateInfo.min_hardware_ver?.isNotEmpty == true && versionCompare(hardwareVer, updateInfo.min_hardware_ver ?? "0") < 0) { // debugPrint("bluetooth -- dfu update hardware ver ${hardwareVer} min ${updateInfo.min_hardware_ver}"); // return; // } // // if (updateInfo.max_hardware_ver?.isNotEmpty == true && versionCompare(hardwareVer, updateInfo.max_hardware_ver ?? "0") > 0) { // debugPrint("bluetooth -- dfu update hardware ver ${hardwareVer} max ${updateInfo.max_hardware_ver}"); // return; // } updateInfo.devices = []; updateInfo.localVer = softwareVer; print("bluetooth -- dfu versionCompare ${versionCompare(softwareVer, updateInfo.ver ?? "0.0")}"); bool cancelable = this.testDfu >= 5 ? true : !isDebugShoe; if (this.testDfu >= 5) { } else { final testVersion = "梁剑波DEBUG专用"; bool willTest = updateInfo.name == testVersion; if (willTest) { if (versionCompare(softwareVer, updateInfo.ver ?? "0.0") == 0) return; } else { if (versionCompare(softwareVer, updateInfo.ver ?? "0.0") >= 0) { return; } } } for (var i = device.length - 1; i >= 0; i--) { updateInfo.devices!.add(device[i]); } verNotifier.value = updateInfo; if (h5gameRNotifier.value == true) return; var context = _context; var value = updateInfo; var closeable = 0; var result = await showDialog( context: context, barrierDismissible: false, builder: (context) => CustomAlertDialog( title: '发现新的鞋子固件', child: WillPopScope( onWillPop: () async { return false; }, child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { closeable++; if (closeable >= 5) { Navigator.of(context).pop(false); } }, child: Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 24.0), child: Text( "${value.name}\n${value.localVer} -> ${value.ver}\n${value.msg}", style: TextStyle(fontSize: 14, color: Color(0xff333333), height: 1.8), )), ), ), textOk: '立即升级', cancelable: cancelable, ok: () => Navigator.of(context).pop(true)), ); if (result != true) return; _dfuController.add(true); await Future.delayed(Duration(seconds: 1)); result = await showDialog( context: context, barrierDismissible: false, builder: (context) => SimpleDialog( children: [ DfuUpdatePage( hardware: value, version: "${value.ver}", ) ], )); } } void parse(List event) { if (event.isEmpty == true) return; if (!_listen) return; ByteDataReader reader = ByteDataReader(); reader.add(event); int flagAA = reader.readUint8(); // int flagBB = reader.readUint8(); // int flagCC = reader.readUint8(); int len = reader.readUint8(); int len1 = 0xFF - reader.readUint8(); // int index = reader.readUint8(); int cmd = reader.readUint8(); // print(" change --> ${flagAA} ${len} ${~len} ${cmd}"); // print(" change --> ${len} ${~len} ${len1}"); // print(" change --> ${index} ${cmd}"); if (!(flagAA == 0xAA)) return; if (len != len1) return; Uint8List byteArray = reader.read(event.length - 5); int ver = 0; for (int i = 0; i < event.length - 1; i++) { ver += event[i]; } if (ver.toUnsigned(8) != event[event.length - 1]) return; parseCmd(cmd, byteArray); } void parseCmd(int cmd, Uint8List byteArray) { // if (isDebugShoe) print(" cmd $cmd data --> $byteArray"); switch (cmd) { case 0x01: _parseAction(byteArray); break; case BLE.BLE_Client_T_MOTION: disconnectTimes = 0; if (appLifecycleState == AppLifecycleState.resumed) { sdk.gameProcess(byteArray); int cmd = sdk.getInteractionCMD(); if (cmd > -1) { _sdkCmdController.add(cmd); } String gameData = sdk.getGameDataStr(); _sdkMotionDataController.add(gameData); if (isDebugShoe) { // print("gameDate $gameData"); writeLog(gameData); } List result = sdk.getMotion(); if (result.any((element) => element > 0)) { if (isDebugShoe) print("sdk -- motion $result"); _sdkMotionController.add(result); } _dataController.add(byteArray); } break; case 0xA0: break; case BLE.BLE_Client_T_UPDATE: _parseQuery(byteArray); break; case BLE.BLE_Client_T_CONNET_R: _connectRightController.add(byteArray.first); break; case BLE.BLE_Client_T_REALTIMESTEP: _parseStepRealtime(byteArray); break; case BLE.BLE_Client_T_DFU: if (_completerSetupDeviceVer?.isCompleted != true) _completerSetupDeviceVer?.complete(true); break; case BLE.BLE_Client_T_CHARGE: //充电 _parseCharge(byteArray); break; case BLE.BLE_Client_T_ERR: //异常 _parseErr(byteArray); break; case BLE.BLE_Client_T_GAMEMODE: break; case BLE.BLE_Client_T_CONNECT_CONFIRM: _parseConnectConfirm(byteArray); break; } } void _parseConnectConfirm(Uint8List byteArray) { ByteDataReader reader = ByteDataReader(); reader.add(byteArray); int state = reader.readUint8(); } void _parseAction(Uint8List byteArray) { ByteDataReader reader = ByteDataReader(); reader.add(byteArray); int action = reader.readUint8(); int t = reader.readUint16(); print("action: $action "); actionNotifier.value = action; } void _parseQuery(Uint8List byteArray) { ByteDataReader reader = ByteDataReader(); reader.add(byteArray); int cmd = reader.readUint8(); if (cmd != BLE_UPDATE.BASIC) { // 右鞋没连上,不处理下面的流程 if (_isRightError != 1) return; } switch (cmd) { case BLE_UPDATE.BASIC: // 设备基本信息 Map info = {}; List name = []; for (var i = 0; i < 64; i++) { name.add(reader.readUint8()); } info['name'] = String.fromCharCodes(name).replaceAll("\u0000", ""); List softwareVer = []; List hardwareVer = []; List device = []; for (var i = 0; i < 2; i++) { List macList = []; for (var j = 0; j < 6; j++) { macList.add(reader.readUint8().toRadixString(16).padLeft(2, "0")); } var mac = macList.join(":").toUpperCase(); print("mac $i = $mac"); for (var k = 0; k < 4; k++) { hardwareVer.add(reader.readUint8()); } softwareVer.add(reader.readUint16()); device.add(mac); } print("bluetooth dfu info $hardwareVer $softwareVer"); if (hardwareVer.length != 8 || softwareVer.length != 2) return; String leftHardware = hardwareVer.sublist(1, 4).join("."); String rightHardware = hardwareVer.sublist(4 + 1, hardwareVer.length).join("."); int compareHardware = versionCompare(leftHardware, rightHardware); String minHardwareVersion = compareHardware < 0 ? leftHardware : rightHardware; print("bluetooth hardwareVer $leftHardware $rightHardware $compareHardware $minHardwareVersion"); String leftSoftware = hardwareVer.sublist(1, 3).join(".") + ".${softwareVer[0]}"; String rightSoftware = hardwareVer.sublist(4 + 1, 4 + 1 + 2).join(".") + ".${softwareVer[1]}"; int compareSoftware = versionCompare(leftSoftware, rightSoftware); String minSoftwareVersion = compareSoftware < 0 ? leftSoftware : rightSoftware; print("bluetooth softwareVer $leftSoftware $rightSoftware $compareSoftware $minSoftwareVersion"); shoeVersion = "$minSoftwareVersion"; info['softwareVer'] = minSoftwareVersion; info['hardwareVer'] = minHardwareVersion; info['mac'] = device.length > 1 ? device.first : ""; info['device'] = device; infoNotifier.value = info; if (versionNotifier.value != minSoftwareVersion) { versionNotifier.value = minSoftwareVersion; checkUpdate(); } if (_uploadLoged != true) { _uploadLoged = true; _uploadLog(); _uploadErr(); } break; case BLE_UPDATE.DATA: // 设备数据(左鞋,右鞋) Map info = {}; for (var i = 0; i < 2; i++) { info['${i}_electricity'] = reader.readUint8(); info['${i}_temperature'] = reader.readUint8(); info['${i}_pressure'] = reader.readUint(4); reader.readUint(4); } if (reader.remainingLength >= 4) { info['0_adc'] = reader.readUint16(); info['1_adc'] = reader.readUint16(); } // [170, 187, 204, 23, 232, 0, 161, 1, 50, 0, 33, 0, 0, 156, 41, 51, 0, 34, 0, 0, 150, 19, 232] dataNotifier.value = info; int electricity = min(info['0_electricity'] ?? 0, info['1_electricity'] ?? 0); electricityNotifier.value = electricity; if (electricity <= 10) { if (electricityTime == null || (electricityTime?.difference(DateTime.now()).inMinutes.abs() ?? 0) >= 60) { if (stateNotifier.value.values.every((element) => element > 1) != true) { ToastUtil.show("鞋子电量不足,请及时充电!"); electricityTime = DateTime.now(); } } } if (isDebugShoe) { _testElectricity(info); } break; case BLE_UPDATE.STEP: // 查询步数 print("bluetooth -- step part ${_partInfo?.ready}"); if (_partInfo == null) return; if (_partInfo?.ready == true) return; if (isDebugShoe) { _testStep(byteArray.toList().join(",")); print("bluetooth -- step data ${byteArray.toList().join(",")}"); } int start = reader.readUint64(); int serialCount = reader.readUint16(); int serial = reader.readUint16(); // if(serialCount == 0){ // _completerStep?.complete(false); // return; // } List data = []; while (reader.remainingLength > 0) { data.add(reader.readUint32()); } if (serial == 0 && data.isNotEmpty) { stepTotalNotifier.value = data.last; } _partInfo!.start = start; _partInfo!.serialCount = serialCount + 1; _partInfo!.data[serial] = data; if (isDebugShoe) print("bluetooth -- step start $start ${DateTime.fromMillisecondsSinceEpoch(start)} read ($serial,$serialCount) --> $data"); if (_partInfo!.data.length == _partInfo!.serialCount) { _partInfo!.ready = true; var partResult = _partInfo!; Future.microtask(() async { await partResult.prepare(); print("bluetooth -- step end ... "); if (_completerStep?.isCompleted != true) _completerStep?.complete(true); }); } break; case BLE_UPDATE.STEP_DELETE: // 步数回调 if (_completerStepDel != null) _completerStepDel!.complete(true); break; } } void _parseStepRealtime(Uint8List byteArray) { ByteDataReader reader = ByteDataReader(); reader.add(byteArray); int cmd = reader.readUint8(); if (cmd == 0) { } else if (cmd == 1) { int left = 0; int right = 0; if (reader.remainingLength > 4) { left = reader.readUint32(); right = reader.readUint32(); } else { left = reader.readUint16(); right = reader.readUint16(); } // print("left: ${left}, right: ${ right} = ${step}"); stepRealtimeNotifier.value = [left, right]; // 已经不是在实时计步页面,但还收到数据,关了! if (stepRealtimePageNotifier.value == false) { write(BLE.BLE_Client_T_REALTIMESTEP, Uint8List.fromList([BLE_CODE_0])); } } } void _parseCharge(Uint8List byteArray) async { ByteDataReader reader = ByteDataReader(); reader.add(byteArray); int cmd = reader.readUint8(); int state = reader.readUint8(); Map _state = Map.from(stateNotifier.value); _state.update(cmd, (value) => state); stateNotifier.value = _state; // print("111111111111111111111111 $_state"); if (_state[0]! <= 1 || _state[1]! <= 1) { if (_showChargeDialog == true) { Navigator.maybePop(_context); } return; } if (_state[0] == 3 && _state[1] == 3) { queryDeviceData(); } if (_state[0]! > 1 && _state[1]! > 1 && _showChargeDialog != true) { _showChargeDialog = true; showDialog( context: _context, barrierColor: Colors.transparent, builder: (context) => SimpleDialog( backgroundColor: Colors.transparent, elevation: 0, children: [ Center( child: Container( constraints: BoxConstraints(maxWidth: 180.0), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(10.0)), color: Colors.black.withOpacity(.75), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Align( child: GestureDetector( child: Padding( padding: const EdgeInsets.all(10.0), child: Image.asset( "lib/assets/img/btn_close_white.png", width: 18, ), ), onTap: () { Navigator.maybePop(context); }, behavior: HitTestBehavior.opaque, ), alignment: Alignment.topRight, ), Padding( padding: const EdgeInsets.fromLTRB(20.0, 5, 20.0, 16.0), child: ValueListenableBuilder>( valueListenable: stateNotifier, builder: (context, val, __) { int totalState = (val[0]! + val[1]!) ~/ 2; List keys = val.keys.toList(); keys.sort(); return Column( children: [ Center( child: Row( children: keys.map((key) { int state = val[key]!; String label = key == 0 ? "left" : "right"; return Padding( padding: EdgeInsets.symmetric(horizontal: 10), child: Image.asset( state == 3 ? "lib/assets/img/pop_icon_${label}full.png" : state == 2 ? "lib/assets/img/pop_icon_${label}charge.png" : "lib/assets/img/pop_icon_${label}fault.png", ), ); }).toList(), mainAxisSize: MainAxisSize.min, ), ), const SizedBox( height: 15, ), ValueListenableBuilder( valueListenable: electricityNotifier, builder: (BuildContext context, int value, Widget? child) { int _power = max(0, (val[0]! > 1 && val[1]! > 1) ? value : 0); return Column( children: [ Text( (_power > 1) ? "$_power%" : "", style: TextStyle(fontSize: 8.0, color: Colors.white), ), const SizedBox( height: 7, ), ClipRRect( child: SizedBox( height: 2, child: LinearProgressIndicator( backgroundColor: Color(0xff656565), valueColor: AlwaysStoppedAnimation(Color(0xff00DC42)), value: _power / 100, ), ), borderRadius: BorderRadius.circular(5), ) ], ); }, ), const SizedBox( height: 11, ), Text( (val[0] == 3 && val[1] == 3) ? "智能鞋充电已完成" : (val[0]! > 1 && val[1]! > 1) ? "智能鞋充电中..." : (val[0]! <= 1 && val[1]! <= 1) ? "智能鞋连接异常" : val[0]! <= 1 ? "左鞋充电连接异常" : val[1]! <= 1 ? "右鞋充电连接异常" : "连接异常", style: TextStyle(fontSize: 12.0, color: Colors.white), ), ], ); }), ), ], )), ) ], )).then((value) => _showChargeDialog = false); } Future.delayed(Duration(seconds: 5)).then((value) { if (_showChargeDialog == true) { Map val = stateNotifier.value; print("_parseCharge $val"); if (val[0]! > 1 && val[1]! > 1) { Navigator.pop(_context); } } }); } Map> errQueue = {}; /// 错误处理 void _parseErr(Uint8List byteArray) async { ByteDataReader r = ByteDataReader(); r.add(byteArray); int leftOrRight = r.readUint8(); int code = r.readUint8(); List msgData = r.read(r.remainingLength); String msg = String.fromCharCodes(msgData); String err = "${leftOrRight == 0 ? "左鞋" : "右鞋"}:$msg"; addErr("$code", err, ""); } void _uploadErr() async { // if (infoNotifier.value.isEmpty) return; if (errQueue.isEmpty) return; var info = infoNotifier.value; // if (info.isEmpty) return; PackageInfo packageInfo = await PackageInfo.fromPlatform(); String mac = info['mac']?.toString() ?? _connectingDevice?.id ?? _deviceId ?? "00:00:00:00:00:00"; String firmware = "${info['softwareVer']}"; String hardware = "${info['hardwareVer']}"; String app_ver = "${packageInfo.version}"; List keys = errQueue.keys.toList(); for (var i = 0; i < keys.length; i++) { String code = keys[i]; Map err = errQueue[code] as Map; String? message = err["message"]; String? detail = err["detail"]; if (isDebugShoe) { await _testErrLog(); } var resp = await api.logHardwareErr(mac, firmware, hardware, app_ver, code, "$message", "$detail ${await Application.getDeviceInfo()}"); if (resp.code != 0) { await Future.delayed(Duration(seconds: 2)); } } } addErr(String code, String? message, String? detail) { if (isDebugShoe) { print("bluetooth -- err $code $message $detail"); } if (!errQueue.containsKey(code)) { errQueue[code] = {"code": code, "message": message, "detail": detail}; _uploadErr(); } } Future _saveInfo(PartInfo? partInfo) async { if (partInfo == null) return; print("bluetooth -- step save ${partInfo.start} ${partInfo.serialCount} ${partInfo.items.length}"); if (partInfo.items.isEmpty == true) return; List _items = []; for (var item in partInfo.items) { _items.add(item); print("bluetooth -- step save ${item.time} ${item.step}"); } await StepDB().insertAll(_items); _queryController.add(true); } Future _uploadInfo(int startTime) async { final now = DateTime.fromMillisecondsSinceEpoch(startTime); final offset = DateTime(now.year, now.month, now.day, now.hour); var list = await StepDB().find(offset.millisecondsSinceEpoch, _deviceId ?? "0"); if (list.isEmpty) return 0; List> data = []; int step = 0; for (var item in list) { int _step = Converter.toInt(item['st']); if (_step > 0 && _step < 10000000) { data.add([_step, item['di'], (item['time'] as int) ~/ 1000]); } step += Converter.toInt(_step); } print("bluetooth -- step upload ${offset.millisecondsSinceEpoch} $startTime $data"); if (step > 5) { var resp = await api.addDaily(data: json.encode(data)); if (resp.code == 0) { // await StepDB().delete(last); return 0; } } return step == 0 ? 0 : -1; // Directory appDocDir = await getApplicationDocumentsDirectory(); // String appDocPath = appDocDir.path; // Directory saveDir = Directory("$appDocPath/step"); // var files = saveDir.listSync(); // for(var file in files){ // var f= new File(file.path); // var content = f.readAsStringSync(); // await api.addDaily(data: content); // file.delete(); // } } // 查询 设备基本信息 Future queryDeviceCharge() async { await write(BLE.BLE_Client_T_CHARGE, Uint8List(0)); } // 查询 设备右鞋 Future queryDeviceRight() async { await write(BLE.BLE_Client_T_CONNET_R, Uint8List.fromList([])); } // 查询 设备基本信息 Future queryDeviceInfo() async { await write(BLE.BLE_Client_T_UPDATE, Uint8List.fromList([BLE_CODE_0])); } // 查询 设备数据 Future queryDeviceData() async { await write(BLE.BLE_Client_T_UPDATE, Uint8List.fromList([BLE_CODE_1])); } // 查询 查询步数 Future queryDeviceStep({bool test = false}) async { if (!isConnected) return; // await Future.delayed(Duration(seconds: 3)); // _queryController.add(true); if (test != true) if (stepTotalTestNotifier.value == 1) return; if (_partInfo != null) { print("bluetooth -- step _partInfo.now ${_partInfo!.now.difference(DateTime.now()).inSeconds}"); if ((_partInfo!.now.difference(DateTime.now()).inSeconds) > -60) { return; } } print("bluetooth -- step init"); _partInfo = PartInfo(_deviceId ?? "0", 0, 0); if (test == true) stepTotalNotifier.value = -1; // await write(BLE.BLE_Client_T_UPDATE, createTime(BLE_UPDATE.STEP)); DateTime now = DateTime.now(); DateTime offset = DateTime(now.year, now.month, now.day, now.hour); print("bluetooth -- create now .. $now - $offset (${offset.millisecondsSinceEpoch})"); ByteDataWriter writer = ByteDataWriter(); writer.writeUint8(BLE_UPDATE.STEP); writer.writeUint16(BLE_CODE_0); writer.writeUint64(now.millisecondsSinceEpoch); await write(BLE.BLE_Client_T_UPDATE, writer.toBytes()); if (_completerStep != null && _completerStep?.isCompleted != true) _completerStep?.complete(false); if (_completerStepDel != null && _completerStepDel?.isCompleted != true) _completerStepDel?.complete(false); _completerStep = Completer(); _completerStepDel = Completer(); var timer = Timer.periodic(Duration(seconds: 1), (timer) { if (_partInfo == null) return; Iterable keys = _partInfo!.data.keys; if (keys.isEmpty) return; for (var i = 1; i < _partInfo!.serialCount; i++) { if (!keys.contains(i)) { print("bluetooth -- step request package $i"); // 请求缺包 ByteDataWriter writer = ByteDataWriter(); writer.writeUint8(BLE_UPDATE.STEP); writer.writeUint16(i); writer.writeUint64(now.millisecondsSinceEpoch); write(BLE.BLE_Client_T_UPDATE, writer.toBytes()); } } }); Timer timeout = Timer(Duration(seconds: 20), () { if (_completerStep?.isCompleted != true) _completerStep?.complete(false); }); var result = await _completerStep!.future; timeout.cancel(); timer.cancel(); int stepTotal = 0; if (result == true) { await _saveInfo(_partInfo); int code = await _uploadInfo(_partInfo!.start); if (code != 0) { code = _partInfo!.items.length; } if (isDebugShoe) { var now = DateTime.now(); _partInfo?.items.forEach((element) { var info = {"t": element.time, "s": element.step, "total": element.absolute}; _testStep("$_deviceId,${now.hour},${now.minute},${now.second},${info['t']},${info['s']},${info['total']}"); }); } if (test == true) { if (_partInfo?.items.isNotEmpty == true) { stepTotal = _partInfo?.items.map((e) => e.step).reduce((value, element) => value + element) ?? 0; } else { stepTotal = 0; } } if (code == 0) { var timer = Timer.periodic(Duration(milliseconds: 1000), (timer) { write(BLE.BLE_Client_T_UPDATE, createTime(BLE_UPDATE.STEP_DELETE)); if (_isRightError != 1) { timer.cancel(); if (_completerStepDel?.isCompleted != true) _completerStepDel?.complete(false); } }); Timer timeout = Timer(Duration(seconds: 60), () { if (_completerStepDel?.isCompleted != true) _completerStepDel?.complete(false); }); await _completerStepDel!.future; timer.cancel(); timeout.cancel(); } _completerStepDel = null; } _partInfo = null; print("bluetooth -- step reset"); stepNotifier.value = stepTotal; } // 固件升级 Future setupDeviceVer(int type) async { _completerSetupDeviceVer = Completer(); int time = 10; Timer timeout = Timer.periodic(Duration(seconds: 1), (t) { try { write(BLE.BLE_Client_T_DFU, Uint8List.fromList([type])); } catch (e) { print(e); } time--; if (time < 0) { if (_completerSetupDeviceVer?.isCompleted != true) _completerSetupDeviceVer?.complete(false); t.cancel(); } }); var result = (await _completerSetupDeviceVer?.future) ?? false; timeout.cancel(); return result; } // 游戏模式 Future setupGameMode(bool mode) async { if (mode == false && h5gameRNotifier.value == true) { return; } print("=================================setupGameMode $mode================================="); gameModeTimer?.cancel(); if (mode) { gameModeTimer = Timer.periodic(Duration(seconds: 30), (timer) { if (this._listen == true) write(BLE.BLE_Client_T_GAMEMODE, Uint8List.fromList([BLE_CODE_1])); }); } await write(BLE.BLE_Client_T_GAMEMODE, mode ? Uint8List.fromList([BLE_CODE_1]) : Uint8List.fromList([BLE_CODE_0])); } Future setupGameMode4h5(bool mode) async { h5gameRNotifier.value = mode; setupGameMode(mode); } Future setupDataDebug(bool mode) async { await write(BLE.BLE_Client_T_GAMEMODE, mode ? Uint8List.fromList([BLE_CODE_1 + 1]) : Uint8List.fromList([BLE_CODE_0])); // // device?.rssi.listen((event) { // rssiNotifier.value = event; // }); // // rssiTimer?.cancel(); // if (mode == true) { // rssiTimer = Timer.periodic(Duration(seconds: 1), (timer) { // device?.requestRssi(); // }); // } } Future vibrate(int vibrate, {int leftOrRight = 0}) async { int duration = min(1000, max(100, vibrate)); ByteDataWriter writer = ByteDataWriter(); writer.writeUint16(duration); writer.writeUint8(leftOrRight); await write(BLE.BLE_Client_T_SHOCK, writer.toBytes()); } Future lighting(int time) async { int duration = min(10000, max(100, time)); ByteDataWriter writer = ByteDataWriter(); writer.writeUint16(duration); await write(BLE.BLE_Client_T_LIGHTING, writer.toBytes()); } Future stepRealTime(bool mode) async { stepRealtimeNotifier.value = []; stepRealtimePageNotifier.value = mode; await write(BLE.BLE_Client_T_REALTIMESTEP, mode ? Uint8List.fromList([BLE_CODE_1]) : Uint8List.fromList([BLE_CODE_0])); stepRealTimeTimer?.cancel(); if (mode == true) { stepRealTimeTimer = Timer.periodic(Duration(seconds: 10), (timer) { write(BLE.BLE_Client_T_REALTIMESTEP, Uint8List.fromList([BLE_CODE_1])); }); } } Uint8List createTime(int cmd) { DateTime now = DateTime.now(); DateTime offset = DateTime(now.year, now.month, now.day, now.hour); print("bluetooth -- create now .. $now - $offset (${offset.millisecondsSinceEpoch})"); ByteDataWriter writer = ByteDataWriter(); writer.writeUint8(cmd); writer.writeUint64(offset.millisecondsSinceEpoch); writer.writeUint8(max(0, min(60, now.minute - offset.minute))); return writer.toBytes(); } Future write(int cmd, Uint8List data, {int test = 5}) async { if (characteristicWrite == null) return; int length = data.length + 5; ByteDataWriter writer = ByteDataWriter(); writer.writeUint8(0xAA); // writer.writeUint8(0xBB); // writer.writeUint8(0xCC); writer.writeUint8(length); writer.writeUint8(0xFF - length); // writer.writeUint8(0x00); writer.writeUint8(cmd); if (data.isNotEmpty) writer.write(data); int ver = writer.toBytes().reduce((value, element) => value + element); writer.writeUint8(ver); Uint8List out = writer.toBytes(); if (isDebugShoe) { print("bluetooth -- out ${cmd.toRadixString(16).padLeft(2, "0")} ${out.map((e) => e.toRadixString(16).padLeft(2, "0")).join(" ").toUpperCase()}"); } // print("out ${out.map((e) => e.toRadixString(16)).join(",")}"); for (var i = 0; i < test; i++) { try { await flutterReactiveBle.writeCharacteristicWithoutResponse(characteristicWrite!, value: out); break; } catch (e) { print(e); await Future.delayed(Duration(milliseconds: 100)); } } } Future writeUintList(Uint8List data) async { if (characteristicWrite == null) return; int length = data.length + 4; ByteDataWriter writer = ByteDataWriter(); writer.writeUint8(0xAA); // writer.writeUint8(0xBB); // writer.writeUint8(0xCC); writer.writeUint8(length); writer.writeUint8(0xFF - length); // writer.writeUint8(0x00); if (data.isNotEmpty) writer.write(data); int ver = writer.toBytes().reduce((value, element) => value + element); writer.writeUint8(ver); Uint8List out = writer.toBytes(); if (isDebugShoe) { print("bluetooth -- out ${out.map((e) => e.toRadixString(16).padLeft(2, "0")).join(" ").toUpperCase()}"); } for (var i = 0; i < 5; i++) { try { await flutterReactiveBle.writeCharacteristicWithoutResponse(characteristicWrite!, value: out); break; } catch (e) { print(e); await Future.delayed(Duration(milliseconds: 100)); } } } _uploadLog() async { var info = infoNotifier.value; if (info.isEmpty == true) return; final api = GetIt.I(); PackageInfo packageInfo = await PackageInfo.fromPlatform(); String os_name = Platform.operatingSystem; String os_ver = Platform.operatingSystemVersion; String phone_brand = ""; String phone_model = ""; try { DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); if (Platform.isAndroid) { AndroidDeviceInfo androidDeviceInfo = await deviceInfo.androidInfo; phone_brand = androidDeviceInfo.brand; phone_model = androidDeviceInfo.model; } else if (Platform.isIOS) { IosDeviceInfo iosDeviceInfo = await deviceInfo.iosInfo; phone_brand = iosDeviceInfo.model; phone_model = iosDeviceInfo.utsname.machine; } } catch (e) { print(e); } api.onConnBt("${info['mac']}", "${info['softwareVer']}", "${info['hardwareVer']}", "${packageInfo.version}+${packageInfo.buildNumber}", os_name, os_ver, "${locale?.languageCode}", phone_brand, phone_model).then((value) => _uploadLoged = true); } _testErrLog() async { Directory? dir = Platform.isAndroid ? await getExternalStorageDirectory() : await getTemporaryDirectory(); if (dir != null) { var now = DateTime.now(); File file = File("${dir.path}/shoe/err_${now.year}_${now.month}_${now.day}.csv"); print("log file: $file $errQueue"); if (!file.parent.existsSync()) { file.parent.createSync(); } errQueue.forEach((k, v) { file.writeAsStringSync("$k, $v\n", mode: FileMode.append, flush: true); }); } } void _testElectricity(Map info) async { Directory? dir = Platform.isAndroid ? await getExternalStorageDirectory() : await getTemporaryDirectory(); if (dir != null) { var now = DateTime.now(); File file = File("${dir.path}/shoe/electricity_${now.year}_${now.month}_${now.day}.csv"); print("log file: $file"); if (!file.parent.existsSync()) { file.parent.createSync(); } file.writeAsStringSync("${now.hour},${now.minute},${now.second},${info['0_electricity']},${info['1_electricity']},${info['0_adc']},${info['1_adc']},${_connectingDevice?.name ?? deviceId}\n", mode: FileMode.append, flush: true); } } void _testStep(String line) async { Directory? dir = Platform.isAndroid ? await getExternalStorageDirectory() : await getTemporaryDirectory(); if (dir != null) { var now = DateTime.now(); File file = File("${dir.path}/shoe/step_${now.year}_${now.month}_${now.day}.csv"); print("log file: $file"); if (!file.parent.existsSync()) { file.parent.createSync(); } file.writeAsStringSync("$line\n", mode: FileMode.append, flush: true); } } void updateName(String name) { DiscoveredDevice? device = deviceNotifier.value; if (device != null) { String newName = name.isNotEmpty ? name : device.name; BluetoothDB().insert(device, mark: newName).then((value) { deviceNameNotifier.value = newName; }); } } } class PartItem { String shoe; int step; int distance; int absolute; int time; PartItem(this.shoe, this.step, this.absolute, this.distance, this.time); Map toJson() { final Map data = new Map(); data['shoe'] = this.shoe; data['step'] = this.step; data['absolute'] = this.absolute; data['distance'] = this.distance; data['time'] = this.time; return data; } } class PartInfo { String shoeId; int start; int serial = 0; int serialCount; List items = []; late DateTime now; late DateTime maxTime; late Map> data; bool ready = false; PartInfo(this.shoeId, this.start, this.serialCount) { now = DateTime.now(); maxTime = DateTime(now.year, now.month, now.day, now.hour); data = {}; } final int offset = 60 * 60 * 1000; prepare() async { print("bluetooth -- step prepare ... "); if (data.isEmpty) return; if (start == 0) return; var current = data[0] ?? []; if (current.isEmpty) return; int currentStep = current.first; print("bluetooth -- step current ... $currentStep"); if (currentStep >= 2147483647) { if (data.values.where((element) => element.first >= 2147483647).length <= 1) { return; } } var db = StepDB(); var history = await db.findHistory(start, shoeId); print("bluetooth -- step history ... $start $history"); int startStep = 0; var p = PartItem(shoeId, 0, currentStep, 0, start - offset); if (history.isNotEmpty == true) { startStep = history.first['st']; if (startStep > currentStep) { await db.deleteAll(); print("bluetooth -- history ... clear"); startStep = currentStep; await db.insert(p); print("bluetooth -- step add history ... ${p.toJson()}"); } } else { startStep = currentStep; await db.insert(p); print("bluetooth -- step add history ... ${p.toJson()}"); } data[data.length] = current; for (var i = 1; i < data.length; i++) { var list = data[i] ?? []; for (int e in list) { int time = start + items.length * offset; int step = max(0, e - startStep); print("bluetooth -- step add $time $e s:$startStep step:$step"); items.add(PartItem(shoeId, step, e, 0, min(time, maxTime.millisecondsSinceEpoch))); startStep = e; } } } }