import 'dart:async'; import 'package:grpc/grpc.dart'; import 'package:trackoffical_app/logger.dart'; import 'package:trackoffical_app/service/api.dart'; import 'package:trackoffical_app/service/app.dart'; import 'package:trackoffical_app/service/game/game_model.dart'; import 'package:trackoffical_app/utils.dart'; import 'package:simple_kalman/simple_kalman.dart'; import '../../model/game_map.dart'; import '../../model/m_position.dart'; import 'plug.dart'; import 'package:get/get.dart'; import 'package:trackoffical_app/pb.dart' as pb; class PlugLocation extends Plug { StreamSubscription? _locationSubscription; final GameModel _model = Get.find(); final GameMap _gameMap; final kalmanLat = SimpleKalman(errorMeasure: 360, errorEstimate: 150, q: 0.9); final kalmanLon = SimpleKalman(errorMeasure: 360, errorEstimate: 150, q: 0.9); final _toUpload = []; PlugLocation({ required GameMap gameMap, required List lastInfo, }) : _gameMap = gameMap { if(lastInfo.isNotEmpty){ for(var one in lastInfo){ var p = MPosition(); p.latitude = one.latitude; p.longitude = one.longitude; p.directionRadian = one.directionRadian; p.timestamp = one.gpsTime.toModel(); _addOnePosition(p); } } } final ApiService _api = Get.find(); var _lastPositionTime = DateTime(2000); final _app = App.to; @override Future init() async { // await setLocationSettings( // askForGooglePlayServices: false, // askForPermission: true, // useGooglePlayServices: false, // accuracy: LocationAccuracy.high, // interval: 500, // askForGPS: true // ); // // final locationStream = onLocationChanged(); final locationStream = _app.locationStream; _app.locationStart(500.milliseconds, 0); _locationSubscription?.cancel(); _locationSubscription = locationStream.listen((event) { onRcvPosition(event); // final lat = event.latitude; // final lon = event.longitude; // if (lat != null && lon != null) { // final p = MPosition(latitude: lat, longitude: lon); // p.timestamp = _app.now; // onRcvPosition(p); // } }); final lastPosition = _model.myPosition.value; if (lastPosition != null) { _gameMap .worldToPixel(lastPosition) .then((value) => _model.myPositionOnMap.value = value); } loopUploadPosition(); } void _addOnePosition(MPosition p){ if (_model.myPositionHistory.isNotEmpty) { final lastP = _model.myPositionHistory.last; final dis = p.distance(lastP); _model.myPositionHistoryLen.value += dis; } _model.myPositionHistory.add(p); } void onRcvPosition(MPosition p) { p.directionRadian = -_model.compassRadiansFused.value; // // 卡尔曼 // if(_isFirstP){ // for(var i = 0; i<10; i++){ // p.latitude = kalmanLat.filtered(p.latitude); // p.longitude = kalmanLon.filtered(p.longitude); // } // _isFirstP=false; // } // // p.latitude = kalmanLat.filtered(p.latitude); // p.longitude = kalmanLon.filtered(p.longitude); // info('${p.latitude} ${p.longitude}'); _model.myPosition.value = p; _model.myPositionHistoryTmp.add(p); if(_model.myPositionHistoryTmp.length>=20){ _model.myPositionHistoryTmp.removeAt(0); } _toUpload.add(p); _gameMap .worldToPixel(p) .then((value) => _model.myPositionOnMap.value = value); // final pTest = MPosition( latitude: 36.649713, // longitude:117.052860,); _gameMap.mapPackage?.isInHotZone(p).then((value) => _model.zone.value=value ); if (_model.isStarted && (!_model.isFinish)) { final now = DateTime.now(); if (now.difference(_lastPositionTime) > 1.seconds) { _addOnePosition(p); _model.gameSrcState.value.pbGameSave.gameGpsInfos.add(p.toPb2()); _lastPositionTime = now; } } } Future loopUploadPosition() async { while (isActive) { await Future.delayed(1.seconds); await uploadPosition(); } } Future uploadPosition() async { final gameId = _model.gameSrcState.value.pbGameData.gameId; final data = []; final distanceMeter = _model.myPositionHistoryLen.value.m; final paceSecondKm = _model.paceSecondKm; if(_toUpload.isNotEmpty){ data.addAll(_toUpload); try { await _api.gameGpsUpload( gameId, data, distanceMeter.round(), paceSecondKm.value.inSeconds); _toUpload.clear(); } catch (e) { warn('GameId[$gameId]上传位置失败: $e'); } } } Future _makeSureUploadAll() async { while (true) { try { await uploadPosition(); return; } on GrpcError catch (e) { if (e.code != StatusCode.unavailable) { return; } error(e); } catch (e) { error(e); return; } await Future.delayed(100.milliseconds); } } @override Future close() async { await _makeSureUploadAll(); super.close(); _app.locationStop(); _locationSubscription?.cancel(); _locationSubscription = null; debug('[位置插件]已关闭'); } }