plug_sport_wear.dart 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import 'dart:async';
  2. import 'package:fixnum/fixnum.dart';
  3. import 'package:grpc/grpc.dart';
  4. import 'package:trackoffical_app/appcore/exercise.dart';
  5. import 'package:trackoffical_app/service/app.dart';
  6. import 'package:trackoffical_app/service/game/game_model.dart';
  7. import 'package:trackoffical_app/service/game/plug.dart';
  8. import 'package:trackoffical_app/service/sport_wear.dart';
  9. import 'package:get/get.dart';
  10. import 'package:pedometer/pedometer.dart';
  11. import 'package:sensor/sensor.dart';
  12. import 'package:trackoffical_app/pb.dart' as pb;
  13. import 'package:sensors_plus/sensors_plus.dart';
  14. import '../../logger.dart';
  15. import '../api.dart';
  16. class PlugSportWear extends Plug{
  17. final SportWearService _sportWearService = Get.find();
  18. final GameModel _model = Get.find();
  19. StreamSubscription<HeartRateMeasurement>? _heartRateMeasurementSubscription;
  20. final _exercise = Exercise();
  21. final _app = App.to;
  22. final hrUploadHistory = <pb.HeartRate>[];
  23. final ApiService _api = Get.find();
  24. var _initStepCount = -1;
  25. /// 上次保存的步数
  26. var _stepOffset = 0;
  27. List<pb.HeartRate> _lastHr = [];
  28. PlugSportWear({required List<pb.HeartRate> lastHr}){
  29. _lastHr = lastHr;
  30. }
  31. @override
  32. Future<void> init() async {
  33. await _exercise.init();
  34. final userProfile = _app.userProfile;
  35. await _exercise.set(
  36. age: userProfile.age.val,
  37. sex: userProfile.sex,
  38. weightKg: userProfile.weightKg.val,
  39. heightCm: userProfile.heightCm.val,
  40. rhr: userProfile.rhr.val.toDouble());
  41. if(_lastHr.isNotEmpty){
  42. for(var one in _lastHr){
  43. final ts = DateTime.fromMillisecondsSinceEpoch(one.timeStampMs.toInt()).toLocal();
  44. await _exercise.hrPush(one.hr, ts);
  45. }
  46. }
  47. _stepOffset = _model.gameSrcState.value.pbGameSave.stepNum;
  48. _model.stepCount.value = _stepOffset;
  49. _heartRateMeasurementSubscription =_sportWearService.heartRateMeasurement
  50. .listen((p0) {
  51. _model.heartRate.value = p0.heartRate;
  52. _exercise.hrPercent(p0.heartRate).then((value) => _model.heartRatePercent.value = value);
  53. final hr = pb.HeartRate()
  54. ..timeStampMs= Int64(p0.timestamp.millisecondsSinceEpoch)
  55. ..hr= p0.heartRate
  56. ;
  57. if(_model.isStarted && (!_model.isFinish)){
  58. _exercise.hrPush(p0.heartRate, p0.timestamp).then((value){
  59. _exercise.ei().then((v) => _model.ei.value = v);
  60. _exercise.ck().then((v) => _model.ck.value = v);
  61. _exercise.kCal().then((v) => _model.kCal.value = v);
  62. _exercise.exerciseKcal().then((v) => _model.exerciseKcal.value = v);
  63. _exercise.hrMax().then((v) => _model.hrMax.value = v);
  64. _exercise.hrMean().then((v) => _model.hrMean.value = v);
  65. });
  66. _model.gameSrcState.value.pbGameSave.gameHrInfos.add(hr);
  67. }
  68. hrUploadHistory.add(hr);
  69. });
  70. loopUploadHr();
  71. final stream = Pedometer.stepCountStream;
  72. _model.stepCount.bindStream(stream.map((event){
  73. if(!_model.isStarted){
  74. return 0;
  75. }
  76. if(_initStepCount == -1){
  77. _initStepCount = event.steps;
  78. return 0;
  79. }
  80. final stepCount = event.steps - _initStepCount + _stepOffset;
  81. _model.gameSrcState.value.pbGameSave.stepNum = stepCount;
  82. return stepCount;
  83. }));
  84. _model.isWalking.value = false;
  85. _model.isWalking.bindStream(Pedometer.pedestrianStatusStream.map((event){
  86. return event.status=='walking';
  87. }));
  88. }
  89. Future<void> loopUploadHr() async {
  90. while (isActive) {
  91. await Future.delayed(1.seconds);
  92. try {
  93. await loopUploadHrOnce();
  94. } catch (e) {
  95. warn('上传运动状态失败: $e');
  96. }
  97. }
  98. }
  99. Future<void> loopUploadHrOnce() async {
  100. if(!_model.isStarted){
  101. return;
  102. }
  103. await uploadHr();
  104. final gameId = _model.gameSrcState.value.pbGameData.gameId;
  105. await _api.gameExerciseStateUpload(
  106. gameId,
  107. _model.hrMean.value,
  108. _model.hrMax.value,
  109. (_model.kCal.value * 1000).round(),
  110. _model.ck.value,
  111. _model.ei.value,
  112. _model.stepCount.value,
  113. _model.heartRatePercent.value.toInt()
  114. );
  115. }
  116. Future<void> uploadHr() async {
  117. if(hrUploadHistory.isNotEmpty){
  118. final gameId = _model.gameSrcState.value.pbGameData.gameId;
  119. await _api.gameHrUpload(gameId, hrUploadHistory);
  120. hrUploadHistory.clear();
  121. }
  122. }
  123. Future<void> _makeSureUploadAll() async {
  124. while (true) {
  125. try {
  126. final gameId = _model.gameSrcState.value.pbGameData.gameId;
  127. await uploadHr();
  128. await _api.gameExerciseStateUpload(
  129. gameId,
  130. _model.hrMean.value,
  131. _model.hrMax.value,
  132. (_model.kCal.value * 1000).round(),
  133. _model.ck.value,
  134. _model.ei.value,
  135. _model.stepCount.value,
  136. _model.heartRatePercent.value.toInt()
  137. );
  138. return;
  139. } on GrpcError catch (e) {
  140. if (e.code != StatusCode.unavailable) {
  141. return;
  142. }
  143. error(e);
  144. } catch (e) {
  145. error(e);
  146. return;
  147. }
  148. await Future.delayed(100.milliseconds);
  149. }
  150. }
  151. @override
  152. Future<void> close() async{
  153. await _makeSureUploadAll();
  154. super.close();
  155. _heartRateMeasurementSubscription?.cancel();
  156. _exercise.close();
  157. debug('[运动数据插件]已关闭');
  158. }
  159. }