location_analyse_controller.dart 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'package:flutter/cupertino.dart';
  4. import 'package:flutter_map/flutter_map.dart';
  5. import 'package:get/get.dart';
  6. import 'package:get/get_core/src/get_main.dart';
  7. import 'package:injectable/injectable.dart';
  8. import 'package:location/base/base_controller.dart';
  9. import 'package:location/data/bean/user_info.dart';
  10. import 'package:location/data/repositories/track_repository.dart';
  11. import 'package:location/sdk/map/map_helper.dart';
  12. import 'package:lottie/lottie.dart';
  13. import 'package:video_player/video_player.dart';
  14. import '../../data/bean/stream_chat_origin_data.dart';
  15. import '../../data/bean/track_daily_bean.dart';
  16. import '../../resource/assets.gen.dart';
  17. import '../../resource/string.gen.dart';
  18. import '../../utils/http_handler.dart';
  19. import '../../widget/gradually_print_text.dart';
  20. import 'location_analyse_util.dart';
  21. @injectable
  22. class LocationAnalyseController extends BaseController
  23. with GetTickerProviderStateMixin {
  24. late final VideoPlayerController locaController;
  25. final RxBool _videoReady = RxBool(false);
  26. UserInfo? userInfo;
  27. TrackDailyBean? errorInfo;
  28. RxList<TrackDailyBean> errorAddr = RxList();
  29. final RxBool _isShowAnalyseAddr = RxBool(false);
  30. bool get isShowAnalyseAddr => _isShowAnalyseAddr.value;
  31. final RxBool _showAnalyseRemainContent = RxBool(false);
  32. bool get showAnalyseRemainContent => _showAnalyseRemainContent.value;
  33. bool get videoReady => _videoReady.value;
  34. StreamSubscription? _streamSubscription;
  35. final GraduallyController graduallyController = GraduallyController();
  36. final RxBool _isShowAnalyseResult = RxBool(false);
  37. bool get isShowAnalyseResult => _isShowAnalyseResult.value;
  38. final RxBool _isRequestedAnalyse = RxBool(false);
  39. bool get isRequestedAnalyse => _isRequestedAnalyse.value;
  40. bool _isRequestingStream = false;
  41. final RxnString _summaryError = RxnString();
  42. String? get summaryError => _summaryError.value;
  43. final Rxn<LottieDelegates> _keywordDelegates = Rxn<LottieDelegates>();
  44. LottieDelegates? get keywordDelegates => _keywordDelegates.value;
  45. final TrackRepository trackRepository;
  46. late AnimationController keywordLottieController =
  47. AnimationController(vsync: this);
  48. bool _keywordLottieReady = false;
  49. Timer? _loopTimer;
  50. Duration loopStart = const Duration(milliseconds: 0);
  51. Duration loopEnd = const Duration(milliseconds: 5032);
  52. LocationAnalyseController(this.trackRepository);
  53. @override
  54. void onInit() {
  55. super.onInit();
  56. _initArgs();
  57. _getKeyword();
  58. _getErrorAddr();
  59. graduallyController.setGraduallyFinishedListener(() {
  60. _isRequestingStream = false;
  61. });
  62. locaController = VideoPlayerController.asset(
  63. Assets.anim.locationAnalyseRobot,
  64. )
  65. ..setVolume(0.0)
  66. ..initialize().then((_) {
  67. _videoReady.value = true;
  68. _startMonitorLoop();
  69. }).catchError((error) {
  70. debugPrint('Error initializing video: $error');
  71. });
  72. }
  73. void _startMonitorLoop() {
  74. locaController.play();
  75. _loopTimer =
  76. Timer.periodic(const Duration(milliseconds: 100), (timer) async {
  77. final position = await locaController.position;
  78. if (position == null) return;
  79. if (position >= loopEnd) {
  80. locaController.seekTo(loopStart);
  81. }
  82. });
  83. }
  84. void _getErrorAddr() {
  85. trackRepository
  86. .trackDailyInterpret(
  87. startTime: errorInfo?.start,
  88. endTime: errorInfo?.end,
  89. userId: userInfo?.id)
  90. .then((list) {
  91. errorAddr.assignAll(list ?? []);
  92. });
  93. }
  94. void _getKeyword() {
  95. trackRepository
  96. .dailyKeyword(
  97. startTime: errorInfo?.start,
  98. endTime: errorInfo?.end,
  99. userId: userInfo?.id)
  100. .then((list) {
  101. //填充分析异常点到lottie占位中,有6个占位点,list如果小于则循环显示,如果超过6个,则随机6个选择,但不能重复出现
  102. if (list != null && list.isNotEmpty) {
  103. _keywordDelegates.value =
  104. LocationAnalyseUtil.convertKeywordDelegates(list);
  105. }
  106. });
  107. }
  108. void _initArgs() {
  109. final info = parameters?['userInfo'];
  110. if (info is UserInfo) {
  111. userInfo = info;
  112. }
  113. final errorInfo = parameters?['errorData'];
  114. if (errorInfo is TrackDailyBean) {
  115. this.errorInfo = errorInfo;
  116. }
  117. }
  118. void back() {
  119. Get.back();
  120. }
  121. void onTrackRefreshClick() {
  122. if (_isRequestingStream) {
  123. return;
  124. }
  125. _startMonitorLoop();
  126. _startKeywordLottieAnimation();
  127. _analyseErrorAddr();
  128. }
  129. String getErrorDistance(TrackDailyBean errorAddr) {
  130. final lastLocation = MapHelper.getLastLocation();
  131. if (lastLocation == null ||
  132. errorAddr.lat == null ||
  133. errorAddr.lng == null) {
  134. return StringName.unopenedPositioning;
  135. }
  136. final distance = MapUtil.calculateLineDistance(lastLocation.longitude,
  137. lastLocation.latitude, errorAddr.lng!, errorAddr.lat!);
  138. return MapUtil.format(distance);
  139. }
  140. void onAnalyseTextComplete() {
  141. _isShowAnalyseAddr.value = true;
  142. Future.delayed(Duration(milliseconds: 700), () {
  143. _showAnalyseRemainContent.value = true;
  144. });
  145. }
  146. void onAnalyseFinishComplete() {
  147. _isShowAnalyseResult.value = true;
  148. _analyseErrorAddr();
  149. }
  150. void _analyseErrorAddr() {
  151. //准备调用分析总结
  152. if (_isRequestingStream) {
  153. return;
  154. }
  155. _isRequestedAnalyse.value = false;
  156. _isRequestingStream = true;
  157. graduallyController.clear();
  158. _summaryError.value = null;
  159. trackRepository
  160. .streamDailyExceptionAnalyse(
  161. startTime: errorInfo?.start,
  162. endTime: errorInfo?.end,
  163. userId: userInfo?.id)
  164. .then((stream) {
  165. _streamSubscription?.cancel();
  166. _streamSubscription = stream.listen((event) {
  167. try {
  168. Map<String, dynamic> json = jsonDecode(event.data);
  169. if (json.isEmpty) {
  170. return;
  171. }
  172. StreamChatOriginData data = StreamChatOriginData.fromJson(json);
  173. if (data.choices == null || data.choices!.isEmpty) {
  174. return;
  175. }
  176. Delta? delta = data.choices![0].delta;
  177. if (delta == null) {
  178. return;
  179. }
  180. graduallyController.append(delta.content ?? "");
  181. } catch (ignore) {}
  182. }, onDone: () {
  183. graduallyController.appendDone();
  184. _setAnalyseSuccess();
  185. }, onError: (error) {
  186. _summaryError.value = "网络错误,请检查网络连接";
  187. debugPrint("error: $error");
  188. _isRequestedAnalyse.value = false;
  189. _isRequestingStream = false;
  190. debugPrintStack();
  191. _setAnalyseSuccess();
  192. });
  193. }).catchError((error) {
  194. _isRequestedAnalyse.value = false;
  195. _isRequestingStream = false;
  196. if (error is ServerErrorException) {
  197. final msg = error.message;
  198. if (msg == null) {
  199. _summaryError.value = error.message ?? "服务出错,请稍后再试";
  200. } else {
  201. graduallyController.append(msg);
  202. graduallyController.appendDone();
  203. }
  204. _setAnalyseSuccess();
  205. } else {
  206. _summaryError.value = "网络错误,请检查网络连接";
  207. debugPrint("error: $error");
  208. debugPrintStack();
  209. }
  210. });
  211. }
  212. void locationKeywordLottieLoad(LottieComposition composition) {
  213. _keywordLottieReady = true;
  214. keywordLottieController.duration = composition.duration;
  215. _startKeywordLottieAnimation();
  216. }
  217. void _startKeywordLottieAnimation() async {
  218. if (!_keywordLottieReady) {
  219. return;
  220. }
  221. await keywordLottieController.animateTo(0.84);
  222. keywordLottieController.repeat(min: 0, max: 0.84, reverse: true);
  223. }
  224. void _setAnalyseSuccess() async {
  225. _loopTimer?.cancel();
  226. if (_keywordLottieReady) {
  227. await keywordLottieController.animateTo(1);
  228. }
  229. _isRequestedAnalyse.value = true;
  230. }
  231. @override
  232. void onClose() {
  233. _loopTimer?.cancel();
  234. _streamSubscription?.cancel();
  235. keywordLottieController.dispose();
  236. super.onClose();
  237. }
  238. }