using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Vinno.IUS.Common.Log;
using Vinno.IUS.Common.Network;
using Vinno.IUS.Common.Network.Leaf;
using Vinno.IUS.Common.Network.Transfer;
using Vinno.vCloud.Protocol.Infrastructures;
using Vinno.vCloud.Protocol.Messages.Client.RemoteDiagnosis;
using Vinno.vCloud.Protocol.Messages.Live;
namespace Vinno.vCloud.Common.FIS.Consultation
{
internal class ConsultationRecipient : ConsultationClient
{
private readonly ManualResetEvent _timingManualResetEvent = new ManualResetEvent(false);
private readonly string _terminalId;
private readonly string _uniqueId;
private string _initiatorId;
private bool _isEmergencyRequesting;
private string _cameraId;
private string _micId;
private string _speakerId;
///
/// Raised when receive a Consultation request from server.
///
public event EventHandler ConsultationRequestArrived;
public ConsultationRecipient(ConsultationLiveVideoProvider videoProvider, string terminalId, string uniqueId) : base(videoProvider)
{
_terminalId = terminalId;
_uniqueId = uniqueId;
}
///
/// Send a join Rtc Consultation Live request
///
///
public LiveStates AcceptConsultation(string cameraHardwareId, string micHardwareId, string speakerHardwareId)
{
var liveState = LiveStates.UnknowException;
try
{
if (CurrentLiveStatus == LiveStates.RecipientAcceptting || CurrentLiveStatus == LiveStates.RecipientAcceptted)
{
Logger.WriteLineInfo($"Consultation Live-- {FLYINSONOUser.Name} accept Consultation failed due to CurrentLiveStatus is {CurrentLiveStatus}");
return LiveStates.TalkIsBusy;
}
CurrentLiveStatus = LiveStates.RecipientAcceptting;
Logger.WriteLineInfo($"Consultation Live-- {FLYINSONOUser.Name} accept Consultation request begin");
using (var request = new AcceptMeetingRequest())
{
request.RoomId = ConsultationInfo.RoomId;
request.InitiatorId = _initiatorId;
request.RecipientId = FLYINSONOUser.AccountId;
request.FeatureSource = FeatureSource.RemoteDiagnosis;
request.Source = FLYINSONOUser.LoginSource;
request.PushLiveState = ConsultationInfo.State;
request.ClientLanguage = GetCurrentLanguage();
var result = ClientLeaf.Send(request);
Logger.WriteLineInfo($"Send AcceptMeetingRequest To Server");
if (result != null)
{
var resultMessage = AcceptMeetingResult.Convert(result);
if (resultMessage != null)
{
liveState = resultMessage.State;
if (resultMessage != null && resultMessage.State == LiveStates.OK)
{
lock (ConsultationInfoLocker)
{
ConsultationInfo.State = resultMessage.State;
}
StartConsultation(cameraHardwareId, micHardwareId, speakerHardwareId);
CurrentLiveStatus = LiveStates.RecipientAcceptted;
return CurrentLiveStatus;
}
Logger.WriteLineInfo($"Consultation Live-- {FLYINSONOUser.Name} accept meeConsultationting request end");
}
}
}
}
catch (ConnectionTimeoutException ex)
{
Logger.WriteLineError($"ConnectionTimeoutException happened when accept Consultation{ex}");
}
catch (Exception ex)
{
Logger.WriteLineError($"Exception happened when accept Consultation{ex}");
}
var recipientId = ConsultationInfo?.ConsultationMemberInfos?.FirstOrDefault(x => x.RoleType == ConsultationRoleType.Recipient)?.Id;
if (recipientId == FLYINSONOUser.AccountId)
{
Hangup(true);
}
else
{
Hangup(false);
}
return liveState;
}
///
/// Send a Rtc Consultation Live rejectation request
///
///
public LiveStates RejectConsultation()
{
Logger.WriteLineInfo($"Consultation Live-- {FLYINSONOUser.Name} reject meet request begin");
CurrentLiveStatus = LiveStates.RecipientRejected;
StopConsultation();
var roomId = ConsultationInfo.RoomId;
var initiatorId = ConsultationInfo.ConsultationMemberInfos.FirstOrDefault(x => x.RoleType == ConsultationRoleType.Initiator)?.Id;
lock (ConsultationInfoLocker)
{
ConsultationInfo = null;
}
Logger.WriteLineInfo($"Consultation Live-- {FLYINSONOUser.Name} reject Consultation request end");
using (var request = new RejectMeetingRequest())
{
request.RoomId = roomId;
request.RecipientId = FLYINSONOUser.AccountId;
request.InitiatorId = initiatorId;
request.LoginSource = FLYINSONOUser.LoginSource;
request.ClientLanguage = GetCurrentLanguage();
var result = ClientLeaf.Send(request);
Logger.WriteLineInfo($"Send RejectMeetingRequest To Server");
if (result != null)
{
var resultMessage = MeetingResult.Convert(result); //拒绝会诊result
if (resultMessage != null)
{
return resultMessage.State;
}
}
}
return LiveStates.UnknowException;
}
protected override void HandleConsultationMemberNotificationArrived(MeetingMemberNotification meetingMemberNotification)
{
if (ConsultationInfo != null)
{
var mem = meetingMemberNotification.Members.FirstOrDefault(f => f.RoleType == MeetingRoleType.Recipient && f.OperationType == ClientMessageOperationType.Delete);
if (mem != null)
{
OnDisconnected(ConsultationDisconnectedType.HangupByOther);
}
else
{
ConsultationMemberChange(meetingMemberNotification);
foreach (var member in meetingMemberNotification.Members)
{
var memberInfo = ConsultationInfo?.ConsultationMemberInfos.FirstOrDefault(f => f.Id == member.Id);
if (member.OperationType == ClientMessageOperationType.Add)
{
ConsultationRoleType roleType = ConsultationRoleType.Initiator;
switch (member.RoleType)
{
case MeetingRoleType.Initiator:
roleType = ConsultationRoleType.Initiator;
break;
case MeetingRoleType.Recipient:
roleType = ConsultationRoleType.Recipient;
break;
case MeetingRoleType.Expert:
roleType = ConsultationRoleType.Expert;
break;
case MeetingRoleType.Administrator:
roleType = ConsultationRoleType.Administrator;
break;
}
var meetingMemberInfo = new ConsultationMemberInfo
{
OperationType = member.OperationType,
RoleType = roleType,
RoomId = member.RoomId,
LoginSource = member.LoginSource,
State = member.State,
Id = member.Id,
Name = member.Name,
DisplayName = member.DisplayName,
Avatar = member.Avatar,
UserSign = member.UserSign,
PushUrl = member.PushUrl,
PullRtmpUrl = member.PullRtmpUrl,
PullFlvUrl = member.PullFlvUrl,
PullHlsUrl = member.PullHlsUrl,
PushLiveState = member.PushLiveState,
};
if (memberInfo == null)
{
ConsultationInfo.ConsultationMemberInfos.Add(meetingMemberInfo);
}
else
{
memberInfo = meetingMemberInfo;
}
}
else if (member.OperationType == ClientMessageOperationType.Delete)
{
if (memberInfo != null)
{
ConsultationInfo?.ConsultationMemberInfos.Remove(memberInfo);
}
}
}
base.HandleConsultationMemberNotificationArrived(meetingMemberNotification);
}
}
}
protected override void OnMessageArrived(object sender, Message e)
{
try
{
base.OnMessageArrived(sender, e);
var startMeetingNotification = StartMeetingNotification.Convert(e); //接收者收到被邀请通知
if (startMeetingNotification != null)
{
HandleStartMeetingNotification(startMeetingNotification);
}
var cancelStartMeetingNotification = CancelStartMeetingNotification.Convert(e); //取消会诊通知
if (cancelStartMeetingNotification != null)
{
HandleCancelStartMeetingNotification();
}
var rejectEmergencyConsultationNotification = RejectEmergencyConsultationNotification.Convert(e); //专家拒绝紧急会诊通知
if (rejectEmergencyConsultationNotification != null)
{
HandleRejectEmergencyConsultationNotification(rejectEmergencyConsultationNotification);
}
}
catch (Exception ex)
{
Logger.WriteLineError($"Consultation Recipient OnMessageArrived Error:{ex}");
}
}
private void HandleRejectEmergencyConsultationNotification(RejectEmergencyConsultationNotification rejectEmergencyConsultationNotification)
{
Logger.WriteLineInfo($"MeetingRecipient OnRejectEmergencyConsultationNotificationArrived,reason:{rejectEmergencyConsultationNotification.Reason}");
if (EmergencyConsultationInfo != null && EmergencyConsultationInfo.SortExperts != null && EmergencyConsultationInfo.ConsultationId == rejectEmergencyConsultationNotification.ConsultationId)
{
_timingManualResetEvent.Set();
var prevSortExpert = EmergencyConsultationInfo.SortExperts.FirstOrDefault(f => f.UserId == rejectEmergencyConsultationNotification.ExpertId);
if (prevSortExpert != null)
{
var nextSortExpert = EmergencyConsultationInfo.SortExperts.FirstOrDefault(f => f.Sort == prevSortExpert.Sort + 1);
if (nextSortExpert != null)
{
if (!CreateAndStartEmergencyConsultation(nextSortExpert))
{
_timingManualResetEvent.Set();
_isEmergencyRequesting = false;
CurrentLiveStatus = LiveStates.RecipientRejected;
OnRejectConsultationNotifyArrived(new ConsultationSubscriberInfo
{
RoomId = null,
Id = rejectEmergencyConsultationNotification.ExpertId
});
lock (EmergencyConsultationLocker)
{
EmergencyConsultationInfo = null;
}
}
}
else
{
_isEmergencyRequesting = false;
CurrentLiveStatus = LiveStates.RecipientRejected;
OnRejectConsultationNotifyArrived(new ConsultationSubscriberInfo
{
RoomId = null,
Id = rejectEmergencyConsultationNotification.ExpertId
});
lock (EmergencyConsultationLocker)
{
EmergencyConsultationInfo = null;
}
}
}
}
}
private void HandleCancelStartMeetingNotification()
{
if (ConsultationInfo != null)
{
OnDisconnected(ConsultationDisconnectedType.CancelledByInitiator);
Logger.WriteLineInfo($"CancelStartMeetingNotification handled");
}
}
private void HandleStartMeetingNotification(StartMeetingNotification startMeetingNotification)
{
Logger.WriteLineInfo("LiveMeeting StartMeetingNotification Arrived");
CurrentLiveStatus = LiveStates.ChatRequestArrived;
var mode = startMeetingNotification.Mode;
var roomId = startMeetingNotification.RoomId;
var protocol = startMeetingNotification.LiveProtocol;
List meetingMemberInfos = new List();
foreach (var member in startMeetingNotification.Members)
{
ConsultationRoleType roleType = ConsultationRoleType.Initiator;
switch (member.RoleType)
{
case MeetingRoleType.Initiator:
roleType = ConsultationRoleType.Initiator;
break;
case MeetingRoleType.Recipient:
roleType = ConsultationRoleType.Recipient;
break;
case MeetingRoleType.Expert:
roleType = ConsultationRoleType.Expert;
break;
case MeetingRoleType.Administrator:
roleType = ConsultationRoleType.Administrator;
break;
}
var meetingMemberInfo = new ConsultationMemberInfo
{
OperationType = member.OperationType,
RoleType = roleType,
RoomId = member.RoomId,
LoginSource = member.LoginSource,
State = member.State,
Id = member.Id,
Name = member.Name,
DisplayName = member.DisplayName,
Avatar = member.Avatar,
UserSign = member.UserSign,
PushUrl = member.PushUrl,
PullRtmpUrl = member.PullRtmpUrl,
PullFlvUrl = member.PullFlvUrl,
PullHlsUrl = member.PullHlsUrl,
PushLiveState = member.PushLiveState,
};
meetingMemberInfos.Add(meetingMemberInfo);
}
var terminalInfo = new TerminalInfo();
var liveTerminal = startMeetingNotification.LiveTerminals.FirstOrDefault();
var channels = new List();
if (liveTerminal.Channels != null)
{
foreach (var channelInfo in liveTerminal.Channels)
{
var channel = new PullChannnelUrl
{
Enable = channelInfo.Enable,
FlvUrl = channelInfo.FlvUrl,
Height = channelInfo.Height,
HlsUrl = channelInfo.HlsUrl,
Name = channelInfo.Name,
RtmpUrl = channelInfo.RtmpUrl,
UserId = channelInfo.UserId,
Width = channelInfo.Width,
};
channels.Add(channel);
}
}
if (liveTerminal.IsMultiChannels)//Windows魔盒
{
var mainTerminalInfo = liveTerminal.Channels.Select(f => f.Name).Contains("Main") ? liveTerminal.Channels.FirstOrDefault(f => f.Name == "Main") : liveTerminal.Channels.FirstOrDefault();
var cameraInfo = liveTerminal.Channels.Count > 1 ? liveTerminal.Channels.FirstOrDefault(x => x.Name != "Main") : null;
terminalInfo = new TerminalInfo()
{
OperationType = liveTerminal.OperationType,
TerminalLiveEnabled = mainTerminalInfo.Enable,
CameraLiveEnabled = cameraInfo?.Enable ?? false,
IsMergeChannel = liveTerminal.IsMergeChannel,
TerminalWidth = mainTerminalInfo.Width,
TerminalHeight = mainTerminalInfo.Height,
TerminalUrl = mainTerminalInfo.RtmpUrl,
CameraWidth = cameraInfo?.Width ?? 0,
CameraHeight = cameraInfo?.Height ?? 0,
CameraUrl = cameraInfo?.RtmpUrl,
TerminalLiveProtocol = liveTerminal.LiveProtocol,
IntegerRoomId = liveTerminal.IntegerRoomId,
TerminalIntegerRoomId = liveTerminal.TerminalIntegerRoomId,
State = liveTerminal.State,
Id = string.IsNullOrEmpty(mainTerminalInfo.UserId) ? liveTerminal.Id : mainTerminalInfo.UserId,
CameraId = cameraInfo?.UserId,
IsMultiChannels = liveTerminal.IsMultiChannels,
PullDataMode = liveTerminal.PullDataMode,
TerminalRoomId = liveTerminal.RoomId,
Channels = channels,
};
}
else
{
terminalInfo = new TerminalInfo()
{
OperationType = liveTerminal.OperationType,
TerminalLiveEnabled = liveTerminal.TerminalLiveEnabled,
CameraLiveEnabled = liveTerminal.CameraLiveEnabled,
IsMergeChannel = liveTerminal.IsMergeChannel,
TerminalWidth = liveTerminal.TerminalWidth,
TerminalHeight = liveTerminal.TerminalHeight,
TerminalUrl = liveTerminal.TerminalUrl,
CameraWidth = liveTerminal.CameraWidth,
CameraHeight = liveTerminal.CameraHeight,
CameraUrl = liveTerminal.CameraUrl,
TerminalLiveProtocol = liveTerminal.LiveProtocol,
IntegerRoomId = liveTerminal.IntegerRoomId,
TerminalIntegerRoomId = liveTerminal.TerminalIntegerRoomId,
State = liveTerminal.State,
Id = liveTerminal.Id,
IsMultiChannels = liveTerminal.IsMultiChannels,
PullDataMode = liveTerminal.PullDataMode,
TerminalRoomId = liveTerminal.RoomId,
Channels = channels,
};
}
lock (ConsultationInfoLocker)
{
var info = new ConsultationInfo(ChatRole.Recipient);
info.AppId = startMeetingNotification.AppId;
info.RoomId = startMeetingNotification.RoomId;
info.LiveTalkingMode = mode;
info.IntegerRoomId = startMeetingNotification.IntegerRoomId;
info.LiveProtocol = startMeetingNotification.LiveProtocol;
info.ConsultationMemberInfos = meetingMemberInfos;
info.TerminalInfo = terminalInfo;
info.IsCurrentTerminal = info.TerminalInfo?.Id == _terminalId;
ConsultationInfo = info;
_initiatorId = ConsultationInfo?.ConsultationMemberInfos?.FirstOrDefault(x => x.RoleType == ConsultationRoleType.Initiator)?.Id;
}
var userName = GetAccoutNameByAccountId(startMeetingNotification.InitiatorId);
Logger.WriteLineInfo($"StartMeetingNotification handled RoomId:{ConsultationInfo.RoomId}");
if (!_isEmergencyRequesting)
{
ConsultationRequestArrived?.Invoke(this, ConsultationInfo);
}
else
{
_timingManualResetEvent.Set();
_isEmergencyRequesting = false;
AcceptConsultation(_cameraId, _micId, _speakerId);
if (CurrentLiveStatus == LiveStates.RecipientAcceptted)
{
OnJoinConsultationNotifyArrived();
}
}
}
public EnumEmergencyConsultationResult RequestEmergencyConsultation(string cameraId, string micId, string speakerId)
{
var liveState = EnumEmergencyConsultationResult.UnknowException;
try
{
if (CurrentLiveStatus == LiveStates.RecipientAcceptting || CurrentLiveStatus == LiveStates.RecipientAcceptted)
{
Logger.WriteLineInfo($"Consultation Live-- {FLYINSONOUser.Name} Request Emergency Consultation failed due to CurrentLiveStatus is {CurrentLiveStatus}");
return EnumEmergencyConsultationResult.IsBusy;
}
if (_isEmergencyRequesting)
{
Logger.WriteLineInfo($"Consultation Live-- {FLYINSONOUser.Name} Request Emergency Consultation failed due to is Requesting Emergency Consultation");
return EnumEmergencyConsultationResult.IsBusy;
}
_cameraId = cameraId;
_micId = micId;
_speakerId = speakerId;
var emergencyConsultationInfo = GetEmergencyConsultationExpertsInfo();
lock (EmergencyConsultationLocker)
{
EmergencyConsultationInfo = emergencyConsultationInfo;
}
switch (EmergencyConsultationInfo.NoExpertCause)
{
case NoExpertCause.ExpertsOfflineAll:
return EnumEmergencyConsultationResult.ExpertsOfflineAll;
case NoExpertCause.ExpertsDeficiencyOfTimeAll:
return EnumEmergencyConsultationResult.ExpertsDeficiencyOfTimeAll;
case NoExpertCause.ExpertsOfflineOrDeficiencyOfTime:
return EnumEmergencyConsultationResult.ExpertsOfflineOrDeficiencyOfTime;
}
if (EmergencyConsultationInfo.SortExperts == null || EmergencyConsultationInfo.SortExperts.Count == 0)
{
return EnumEmergencyConsultationResult.ExpertsIsNotExist;
}
CurrentLiveStatus = LiveStates.InitiatorRequestingChat;
Logger.WriteLineInfo($"Consultation Live-- {FLYINSONOUser.Name} request Emergency Consultation request begin");
if (CreateAndStartEmergencyConsultation(EmergencyConsultationInfo.SortExperts.FirstOrDefault()))
{
_isEmergencyRequesting = true;
return EnumEmergencyConsultationResult.OK;
}
else
{
_isEmergencyRequesting = false;
_timingManualResetEvent.Set();
return EnumEmergencyConsultationResult.Failed;
}
}
catch (Exception ex)
{
Logger.WriteLineError($"Exception happened when RequestEmergencyConsultation Consultation{ex}");
}
_isEmergencyRequesting = false;
return liveState;
}
private void StartTimingTask()
{
_timingManualResetEvent.Reset();
Task.Run(() =>
{
try
{
var currentSort = EmergencyConsultationInfo.CurrentSort;
var currentExpert = EmergencyConsultationInfo.SortExperts.FirstOrDefault(f => f.Sort == currentSort);
Logger.WriteLineInfo($"Consultation Recipient Timing Task Start,Current Sort is {currentSort},ExpertName:{currentExpert?.UserName},ExpertId:{currentExpert?.UserId}");
if (!_timingManualResetEvent.WaitOne(20000))
{
Logger.WriteLineError($"Consultation Emergency Consultation Call Time out,Will Cancel and Request Next Automatic");
if (EmergencyConsultationInfo != null)
{
CancelEmergencyConsultation();
var emergencyConsultationInfo = GetEmergencyConsultationExpertsInfo();
lock (EmergencyConsultationLocker)
{
EmergencyConsultationInfo = emergencyConsultationInfo;
}
var nextSortExpert = EmergencyConsultationInfo.SortExperts.FirstOrDefault(f => f.Sort == currentSort + 1);
if (nextSortExpert != null)
{
if (CreateAndStartEmergencyConsultation(nextSortExpert))
{
_isEmergencyRequesting = true;
}
else
{
_timingManualResetEvent.Set();
_isEmergencyRequesting = false;
CurrentLiveStatus = LiveStates.RecipientRejected;
OnRejectConsultationNotifyArrived(new ConsultationSubscriberInfo
{
RoomId = null,
Id = currentExpert?.UserId,
});
lock (EmergencyConsultationLocker)
{
EmergencyConsultationInfo = null;
}
}
}
else
{
_timingManualResetEvent.Set();
_isEmergencyRequesting = false;
CurrentLiveStatus = LiveStates.RecipientRejected;
OnRejectConsultationNotifyArrived(new ConsultationSubscriberInfo
{
RoomId = null,
Id = currentExpert?.UserId,
});
lock (EmergencyConsultationLocker)
{
EmergencyConsultationInfo = null;
}
}
}
}
else
{
Logger.WriteLineInfo($"Consultation Recipient Timing Task Ended");
}
}
catch (Exception ex)
{
Logger.WriteLineError($"ConsultationRecipient Timing Task Error:{ex}");
}
});
}
///
/// 发起紧急会诊接口
///
private bool CreateAndStartEmergencyConsultation(EmergencyConsultationExpertInfo expertDoctor)
{
try
{
if (expertDoctor == null)
{
Logger.WriteLineError($"Consultation Recipient CreateAndStartEmergencyConsultation Failed,ExpertDoctor is null");
return false;
}
EmergencyConsultationInfo.CurrentSort = expertDoctor.Sort;
StartTimingTask();
using (var request = new CreateAndStartEmergencyConsultationRequest())
{
request.EquCode = _uniqueId;
request.ApplyDoctor = FLYINSONOUser.Name;
request.ExpertDoctor = expertDoctor.UserName;
var result = ClientLeaf?.Send(request);
if (result != null)
{
var resultMessage = CreateAndStartEmergencyConsultationResult.Convert(result);
if (resultMessage != null)
{
EmergencyConsultationInfo.ConsultationId = resultMessage.Id;
return resultMessage.IsSuccess;
}
}
}
}
catch (Exception ex)
{
Logger.WriteLineError($"CreateAndStartEmergencyConsultation Error:{ex}");
}
return false;
}
public LiveStates CancelEmergencyConsultation()
{
var livestates = LiveStates.Failed;
try
{
Logger.WriteLineInfo($"Consultation Recipient-- {FLYINSONOUser.Name} CancelEmergencyConsultation begin");
using (var request = new CancelEmergencyConsultationRequest())
{
request.ConsultationId = EmergencyConsultationInfo.ConsultationId;
request.ExpertId = EmergencyConsultationInfo.SortExperts.FirstOrDefault(x => x.Sort == EmergencyConsultationInfo.CurrentSort)?.UserId;
var result = ClientLeaf.Send(request);
if (result != null)
{
var resultMessage = CancelEmergencyConsultationResult.Convert(request);
if (resultMessage != null)
{
if (resultMessage.IsSuccess)
{
CurrentLiveStatus = LiveStates.Cancelled;
}
}
}
}
lock (EmergencyConsultationLocker)
{
EmergencyConsultationInfo = null;
}
_timingManualResetEvent.Set();
_isEmergencyRequesting = false;
Logger.WriteLineInfo($"Consultation Recipient-- {FLYINSONOUser.Name} CancelEmergencyConsultation end");
return livestates;
}
catch (Exception ex)
{
Logger.WriteLineError($"Consultation Recipient-- {FLYINSONOUser.Name} CancelEmergencyConsultation error:{ex}");
return LiveStates.UnknowException;
}
}
public override void Dispose()
{
Logger.WriteLineInfo("Consultation Recipient is dispose begin");
_timingManualResetEvent?.Set();
_isEmergencyRequesting = false;
base.Dispose();
Logger.WriteLineInfo("Consultation Recipient is dispose end");
}
}
}