123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523 |
- import 'dart:async';
- import 'package:fis_common/event/event_type.dart';
- import 'package:fis_common/index.dart';
- import 'package:fis_i18n/i18n.dart';
- import 'package:fis_jsonrpc/rpc.dart';
- import 'package:fis_lib_media_rt/fis_lib_media_rt.dart';
- import 'package:flutter/services.dart';
- import 'package:flyinsonolite/consultation/liveconsultation/controllers/livememberscontroller.dart';
- import 'package:flyinsonolite/consultation/liveconsultation/controllers/mediacontroller.dart';
- import 'package:flyinsonolite/consultation/records/controllers/consultationdetailcontroller.dart';
- import 'package:flyinsonolite/consultation/records/controllers/consultationlistcontroller.dart';
- import 'package:flyinsonolite/consultation/liveconsultation/models/liveconsultationstate.dart';
- import 'package:flyinsonolite/consultation/liveconsultation/controllers/liveloadingcontroller.dart';
- import 'package:flyinsonolite/consultation/liveconsultation/controllers/memberlisteners.dart';
- import 'package:flyinsonolite/consultation/liveconsultation/controllers/whiteboardcontroller.dart';
- import 'package:flyinsonolite/controls/prompt.dart';
- import 'package:flyinsonolite/jsonrpc/fisLib/services/consultation.m.dart';
- import 'package:flyinsonolite/jsonrpc/fisLib/services/log.m.dart';
- import 'package:flyinsonolite/helpers/dialoghelper.dart';
- import 'package:flyinsonolite/jsonrpc/jsonrpcclient.dart';
- import 'package:flyinsonolite/jsonrpc/jsonrpcclientforFISLib.dart';
- import 'package:flyinsonolite/infrastructure/logger.dart';
- import 'package:flyinsonolite/managers/interfaces/iappointmentmanager.dart';
- import 'package:flyinsonolite/managers/interfaces/idevicemanager.dart';
- import 'package:flyinsonolite/managers/interfaces/iliveconsultationmanager.dart';
- import 'package:flyinsonolite/consultation/records/models/consultationmember.dart';
- import 'package:flyinsonolite/consultation/records/models/liveconsultation.dart';
- import 'package:flyinsonolite/infrastructure/storage.dart';
- import 'package:flyinsonolite/helpers/parameterhelper.dart';
- import 'package:get/get.dart';
- import 'recordingcontroller.dart';
- class LiveConsultationController extends GetxController {
- final ILiveConsultationManager _liveConsultationManager =
- Get.find<ILiveConsultationManager>();
- final IAppointmentManager _appointmentManager =
- Get.find<IAppointmentManager>();
- bool _isJoin = false;
- //String? _consultationCode;
- List<ConsultationMember>? _oldMemberList;
- late RecordingController recordingController;
- late MediaController mediaController;
- late LiveLoadingController loadingController;
- late LiveMembersController membersController;
- late WhiteboardController whiteboardController;
- late MemberListeners memberListeners;
- ConsultationDetailDTO? consultationDetail;
- final state = LiveConsultationState();
- bool get isEmergency =>
- consultationDetail == null ? false : consultationDetail!.isEmergency;
- bool get isRtmpMode =>
- FRTMediaConfig.serviceType == FRealTimeMediaServiceType.ntRtmp;
- ///设备频道加入房间事件
- //final FEventHandler<IPlayChannel> onDeviceChannelAdd = FEventHandler<IPlayChannel>();
- //FEventHandler<bool> onSwitchPosition = FEventHandler<bool>();
- /// 获取到清除事件通知
- FEventHandler<String> onClearCanavsHandler = FEventHandler<String>();
- /// 白板数据接收
- FEventHandler<String> get onWhiteboardDataReceive =>
- _liveConsultationManager.onWhiteboardDataReceive;
- LiveConsultationController({bool isJoin = false}) {
- _isJoin = isJoin;
- mediaController = MediaController(this);
- loadingController = LiveLoadingController(this);
- membersController = LiveMembersController(this);
- memberListeners = MemberListeners(this);
- recordingController = RecordingController(this);
- whiteboardController = WhiteboardController(this);
- consultationDetail = _liveConsultationManager.consultationDetail;
- state.consultationDetail = consultationDetail;
- }
- @override
- void onReady() async {
- try {
- super.onReady();
- await _initDataAsync();
- ///初始化截图列表
- await recordingController.initCaptureImageListAsync();
- } catch (ex) {
- await handlerErrorAsync(
- ex, 'LiveConsultationController onReady error:$ex');
- }
- }
- @override
- void onClose() async {
- recordingController.dispose();
- await _destroyMemoryAsync();
- super.onClose();
- }
- ///获取成员播放频道
- IChannel? getUserMemberPreviewChannel(ConsultationMember member) {
- return mediaController.getUserMemberPreviewChannel(member);
- }
- ///初始化数据
- Future _initDataAsync() async {
- var parameters = ParameterHelper.parameterMap;
- if (parameters.isNotEmpty) {
- var isJoinStr = parameters["isJoin"] ?? '';
- _isJoin = (isJoinStr == 'true' || isJoinStr == 'True');
- }
- await _initConsultationInfo();
- if (state.liveConsultation.value == null) {
- return;
- }
- if (!isRtmpMode) {
- await membersController.startHeartbeatAsync();
- }
- var deviceMember = state.liveConsultation.value!.memberLiveDatas
- .firstWhereOrNull(
- (element) => element.memberType == LiveMemberEnum.Device);
- if (deviceMember != null && deviceMember.isOnline) {
- loadingController.startLoading();
- } else if (deviceMember?.isOnline == false) {
- loadingController.showRetryButton();
- Future.delayed(const Duration(milliseconds: 1000), () {
- PromptBox.toast(i18nBook.errorCodes.errorCode806.t);
- });
- }
- if (deviceMember != null) {
- var deviceCode = deviceMember.id ?? '';
- final deviceManager = Get.find<IDeviceManager>();
- var deviceInfo = await deviceManager.getDeviceInfoAsync(deviceCode);
- if (deviceInfo != null && deviceInfo.deviceType != 'US') {
- state.isUSDevice = false;
- }
- }
- memberListeners.addListeners();
- await membersController.initWaitInLineForConsultationPatientsAsync();
- await mediaController.initAsync();
- }
- ///初始化会诊信息
- Future<void> _initConsultationInfo({bool isReload = false}) async {
- try {
- if (consultationDetail == null) {
- return;
- }
- _liveConsultationManager.currentConsultationCode =
- consultationDetail!.consultationCode!;
- if (!isReload) {
- Storage.isConsultating.value = true;
- state.liveConsultation.value = await _liveConsultationManager
- .startConsultationAsync(consultationDetail!, isJoin: _isJoin);
- if (isRtmpMode) {
- await startRtmpPushing();
- }
- var interactiveBoardDatas =
- state.liveConsultation.value?.interactiveBoardDatas ?? [];
- List<String> initBoardDatas = [];
- for (var interactiveBoardData in interactiveBoardDatas) {
- initBoardDatas.add(interactiveBoardData.boardData ?? '');
- }
- state.initBoardDatas.value = initBoardDatas;
- var memberLiveDatas = state.liveConsultation.value?.memberLiveDatas;
- if (memberLiveDatas != null) {
- for (var member in memberLiveDatas) {
- if (!member.isOnline) {
- member.status.value = MemberStatus.Offline;
- if (member.memberType == LiveMemberEnum.Device) {
- loadingController.showRetryButton();
- }
- } else if (member.isBusy) {
- member.status.value = MemberStatus.Busyline;
- } else if (member.memberType == LiveMemberEnum.User &&
- member.status.value == MemberStatus.Default) {
- member.status.value = MemberStatus.Calling;
- }
- }
- }
- } else {
- var interactiveBoardDatas =
- state.liveConsultation.value?.interactiveBoardDatas ?? [];
- List<String> initBoardDatas = [];
- for (var interactiveBoardData in interactiveBoardDatas) {
- initBoardDatas.add(interactiveBoardData.boardData ?? '');
- }
- state.initBoardDatas.value = initBoardDatas;
- var memberLiveDatas = state.liveConsultation.value?.memberLiveDatas;
- if (memberLiveDatas != null) {
- for (var member in memberLiveDatas) {
- if (!member.isOnline) {
- member.status.value = MemberStatus.Offline;
- if (member.memberType == LiveMemberEnum.Device) {
- loadingController.showRetryButton();
- }
- } else if (member.isBusy) {
- member.status.value = MemberStatus.Busyline;
- } else if (member.memberType == LiveMemberEnum.User &&
- member.status.value == MemberStatus.Default) {
- member.status.value = MemberStatus.Calling;
- }
- }
- }
- }
- } on JsonRpcException {
- await Future.delayed(const Duration(milliseconds: 1500));
- await postProcessAsync();
- } catch (e) {
- await loggerAsyn(FISDeviceLogCategory.Error,
- 'LiveConsultationController _initConsultationInfo ex:$e');
- }
- }
- Future<void> startRtmpPushing() async {
- try {
- var fisLiveConsultationRequestDTO = FISStartOnlyForRtmpPushingDTO(
- consultationCode: consultationDetail!.consultationCode!,
- token: Storage.user.token,
- rtmpPushingUrl: state.liveConsultation.value?.memberLiveDatas
- .firstWhereOrNull(
- (element) => element.id == Storage.user.userCode)
- ?.liveData
- ?.rtmpPushUrl);
- await jsonRpcProxyForFISLib.consultation
- .startOnlyForRtmpPushingFromFlutter(fisLiveConsultationRequestDTO);
- } catch (ex) {
- await handlerErrorAsync(ex, "Push RTMP Error:$ex");
- }
- }
- Future _destroyMemoryAsync() async {
- //stopFullRecording();
- await _unsetMobilePreferredOrientationsAsync();
- membersController.destroyMemory();
- memberListeners.dispose();
- await mediaController.exit();
- //onSwitchPosition.dispose();
- }
- Future _unsetMobilePreferredOrientationsAsync() async {
- if (FPlatform.isAndroid || FPlatform.isIOS) {
- // 恢复竖屏
- await SystemChrome.setPreferredOrientations(
- [
- DeviceOrientation.portraitUp, // 竖屏 Portrait 模式
- DeviceOrientation.portraitDown,
- ],
- );
- SystemChrome.setPreferredOrientations([
- DeviceOrientation.portraitUp,
- ]);
- SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
- overlays: SystemUiOverlay.values);
- SystemChrome.setSystemUIChangeCallback((systemOverlaysAreVisible) async {
- // do nothing
- });
- }
- }
- ///离开房间
- void exitConsultation() {
- DialogHelper.showConfirmDialog(
- Get.context!, i18nBook.realTimeConsultation.confirmLeaveConsultation.t,
- () async {
- try {
- Get.close(1);
- var result = await _liveConsultationManager.leaveLiveConsultationAsync(
- consultationDetail!.consultationCode ?? '');
- var consultationCode = result.consultationCode ?? '';
- if (consultationCode.isNotEmpty) {
- await loggerAsyn(FISDeviceLogCategory.Info,
- '$consultationCode endConsultation success');
- } else {
- await loggerAsyn(FISDeviceLogCategory.Info,
- '$consultationCode endConsultation failed');
- }
- await jsonRpcProxy.user
- .removeUserSingleTokenAsync(RemoveUserSingleTokenRequest(
- token: Storage.user.token,
- wSToken: "${Storage.user.token}_1",
- ));
- } finally {
- await postProcessAsync();
- }
- });
- }
- ///对方离开通知到自己时调用
- Future<void> leaveConsultationAsync() async {
- try {
- if (isRtmpMode) {
- await stopRtmpPushing();
- } else {
- var result = await _liveConsultationManager.leaveLiveConsultationAsync(
- consultationDetail!.consultationCode ?? '');
- var consultationCode = result.consultationCode ?? '';
- if (consultationCode.isNotEmpty) {
- await loggerAsyn(FISDeviceLogCategory.Info,
- '$consultationCode endConsultation success');
- } else {
- await loggerAsyn(FISDeviceLogCategory.Info,
- '$consultationCode endConsultation failed');
- }
- }
- await jsonRpcProxy.user
- .removeUserSingleTokenAsync(RemoveUserSingleTokenRequest(
- token: Storage.user.token,
- wSToken: "${Storage.user.token}_1",
- ));
- } finally {
- await postProcessAsync();
- }
- }
- Future<void> sendFlutterLiveConsultationInfo() async {
- try {
- await jsonRpcProxyForFISLib.consultation.sendFlutterLiveConsultationInfo(
- FISFlutterLiveConsultationInfo(
- isStartLiveConsultation: false,
- token: Storage.user.token,
- consultationCode: "",
- liveConsultationFlutterUrl: ""));
- } catch (ex) {
- await loggerAsyn(FISDeviceLogCategory.Error,
- "LiveConsultationController sendFlutterLiveConsultationInfo Error:$ex");
- }
- }
- Future<void> stopRtmpPushing() async {
- try {
- await jsonRpcProxyForFISLib.consultation
- .stopOnlyForRtmpPushingFromFlutter();
- } catch (ex) {
- await handlerErrorAsync(ex, "StopRtmpPushing Error:$ex");
- }
- }
- Future<void> addMember() async {
- return await membersController.addMember();
- }
- ///取消呼叫
- Future<void> cancelCall(String targetId) async {
- return await membersController.cancelCall(targetId);
- }
- ///再次呼叫
- Future<void> callAgain(String targetId) async {
- return await membersController.callAgain(targetId);
- }
- ///重新加载病人信息,房间不变
- Future<void> reload(LiveConsultation consultation) async {
- await sendInteractiveBoardDataAsync('', true);
- //图像列表置空
- for (ConsultationMember m
- in state.liveConsultation.value?.memberLiveDatas ?? []) {
- if (m.memberType == LiveMemberEnum.User) {
- onClearCanavsHandler.emit('', m.id ?? '');
- }
- }
- state.captureImageList.value = [];
- state.liveConsultation.value = consultation;
- _liveConsultationManager.liveConsultation = consultation;
- var newConsultationDetail = await _appointmentManager
- .findConsultationDetailAsync(consultation.consultationCode!);
- _liveConsultationManager.consultationDetail = newConsultationDetail!;
- consultationDetail = newConsultationDetail;
- try {
- if (isRtmpMode || Storage.isSecondView) {
- //第二窗口或者RTMP推流时需要通知FIS,告知当前的ConsultationCode,退出会诊时以及RTMP心跳包都要用到
- await jsonRpcProxyForFISLib.consultation
- .changeConsultationCodeOnlyForRtmpPushingFromFlutter(
- FISChangeLiveConsultationDTO(
- token: Storage.user.token,
- consultationCode: consultation.consultationCode,
- members: []));
- }
- } catch (ex) {
- await handlerErrorAsync(
- ex, "changeConsultationCodeOnlyForRtmpPushingFromFlutter Error:$ex");
- }
- //_consultationCode = consultation.consultationCode;
- _oldMemberList = state.liveConsultation.value!.memberLiveDatas.toList();
- ConsultationMember? oldDeviceInfo = _oldMemberList?.firstWhereOrNull(
- (element) => element.memberType == LiveMemberEnum.Device);
- ConsultationMember? newDeviceInfo = consultation.memberLiveDatas
- .firstWhereOrNull(
- (element) => element.memberType == LiveMemberEnum.Device);
- if (newDeviceInfo != null) {
- consultation.memberLiveDatas.remove(
- newDeviceInfo); //之所以移除新的DeviceInfo是因为设备是不变的,且通知给的少了VideoDeviceInfo的信息
- }
- if (oldDeviceInfo != null) {
- consultation.memberLiveDatas.add(oldDeviceInfo);
- }
- state.liveConsultation.value = consultation;
- _initConsultationInfo(isReload: true);
- }
- ///会诊设备重试进入房间
- Future<void> retryLoadDevice() async {
- try {
- loadingController.startLoading();
- await _liveConsultationManager
- .retryLoadDeviceAsync(consultationDetail!.consultationCode!);
- } catch (e) {
- await loggerAsyn(FISDeviceLogCategory.Error,
- 'LiveConsultationController retryLoadDevice ex:$e');
- }
- }
- void updateSelectedPatient(ConsultationPageDTO? consultationPageDTO) {
- if (consultationDetail!.consultationCode ==
- consultationPageDTO!.consultationCode) {
- return;
- }
- DialogHelper.showConfirmDialog(
- Get.context!, i18nBook.remedical.confirmCloseCurrentConsultation.t,
- () async {
- Get.close(1);
- await onSelectedPatientChangeAsync(consultationPageDTO);
- });
- }
- ///更换病人
- Future<void> onSelectedPatientChangeAsync(
- ConsultationPageDTO? consultationPageDTO) async {
- if (consultationPageDTO == null) {
- return;
- }
- try {
- await sendInteractiveBoardDataAsync('', true);
- //图像列表置空
- for (ConsultationMember m
- in state.liveConsultation.value?.memberLiveDatas ?? []) {
- if (m.memberType == LiveMemberEnum.User) {
- onClearCanavsHandler.emit('', m.id ?? '');
- }
- }
- _liveConsultationManager.currentConsultationCode =
- consultationPageDTO.consultationCode!;
- final originalCode = consultationDetail!.consultationCode!;
- var result = await _liveConsultationManager.changeConsultationAsync(
- originalCode, consultationPageDTO.consultationCode!);
- var liveConsultation = LiveConsultation(
- consultationCode: result.consultationCode,
- roomNo: result.roomNo,
- initiatorCode: result.initiatorCode,
- );
- liveConsultation.consultationMemberConvert(result.memberLiveDatas ?? []);
- state.liveConsultation.value = liveConsultation;
- _liveConsultationManager.liveConsultation = liveConsultation;
- var newConsultationDetail = await _appointmentManager
- .findConsultationDetailAsync(consultationPageDTO.consultationCode!);
- _liveConsultationManager.consultationDetail = newConsultationDetail!;
- consultationDetail = newConsultationDetail;
- await _initConsultationInfo(isReload: true);
- await membersController.initWaitInLineForConsultationPatientsAsync();
- } catch (ex) {
- await handlerErrorAsync(ex,
- "LiveConsultationController onSelectedPatientChangeAsync error: $ex");
- }
- }
- /// 发送白板数据
- Future<void> sendInteractiveBoardDataAsync(String boardData,
- [bool isClear = false]) async {
- await _liveConsultationManager.sendInteractiveBoardDataAsync(
- consultationDetail!.consultationCode!, boardData, isClear);
- }
- /// 当前颜色
- Color currectColor() {
- return whiteboardController.currectColor();
- }
- /// 截取一帧 vid
- Future captureOneFrameAsync() async {
- recordingController.captureOneFrameAsync();
- }
- ///开启录制
- Future startRecordingAsync() async {
- await recordingController.startRecordingAsync();
- }
- ///结束录制
- Future endRecordingAsync() async {
- await recordingController.endRecordingAsync();
- }
- //后处理
- Future postProcessAsync() async {
- if (Storage.isSecondView) {
- await sendFlutterLiveConsultationInfo();
- }
- _liveConsultationManager.currentConsultationCode = "";
- Storage.isConsultating.value = false;
- if (Storage.isSecondView) {
- Get.close(1);
- } else {
- if (Storage.isLogouting) {
- Get.close(1);
- } else {
- await Future.delayed(const Duration(milliseconds: 500));
- Get.close(1);
- var consultationListController = Get.find<ConsultationListController>();
- await consultationListController.refreshAsync();
- var consultationDetailController =
- Get.find<ConsultationDetailController>();
- await consultationDetailController.onTabChangedAsync(0);
- }
- }
- }
- }
|