location_analyse_controller.dart 8.0 KB

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