776 lines
28 KiB
C#
776 lines
28 KiB
C#
|
using Independentsoft.Sip;
|
|||
|
using Independentsoft.Sip.Sdp;
|
|||
|
using SocketIOComponent;
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Net;
|
|||
|
using System.Net.Sockets;
|
|||
|
using System.Threading.Tasks;
|
|||
|
using SipComponent.Linx;
|
|||
|
|
|||
|
namespace SipComponent
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Class used to make SD talk to Linx using Adi's protocol
|
|||
|
/// <para>Accepts more than one sip call at a time</para>
|
|||
|
/// </summary>
|
|||
|
public class SipClientClassSD : SipClientClass
|
|||
|
{
|
|||
|
|
|||
|
#region Fields
|
|||
|
|
|||
|
private Dictionary<string, CallInfo> _linxDevicesToRespondTo = new Dictionary<string, CallInfo>();
|
|||
|
private Dictionary<string, CallInfo> _linxDevicesToCall = new Dictionary<string, CallInfo>();
|
|||
|
private object _lockerCallingLinx = new object();
|
|||
|
private object _lockerCalledLinx = new object();
|
|||
|
private Dictionary<string, TimerWithTag<LinxIDInfo>> _hangTimeTimers = new Dictionary<string, TimerWithTag<LinxIDInfo>>();
|
|||
|
private object _lockerlinxGroups = new object();
|
|||
|
private HashSet<int> _linxGroupsInDialogWith = new HashSet<int>();
|
|||
|
/// <summary>
|
|||
|
/// Simulated hangTime duration, in ms
|
|||
|
/// </summary>
|
|||
|
private readonly int _hangTimeDuration = 3000;
|
|||
|
|
|||
|
// Socket.IO class
|
|||
|
private SocketIOClass _socketIOClass = null;
|
|||
|
private bool _sendArsOnOff = true;
|
|||
|
|
|||
|
// Random for sending seqIDs and other things....
|
|||
|
private Random _rand = new Random();
|
|||
|
|
|||
|
// Sip message generator
|
|||
|
private LinxSipMessageGenerator _sipMessageGenerator = new LinxSipMessageGenerator();
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
// constructor
|
|||
|
/// <summary>
|
|||
|
/// Constructor for the SipClientClassSD
|
|||
|
/// </summary>
|
|||
|
/// <param name="sipDomain">domain name, name or IP address of sip server</param>
|
|||
|
/// <param name="sipDomainPort">port number of the sip server</param>
|
|||
|
/// <param name="localSipPort">port number of the local computer used for sip protocol</param>
|
|||
|
/// <param name="userName">user name on the sip server</param>
|
|||
|
/// <param name="password">password on the sip server</param>
|
|||
|
/// <param name="registrationInterval">interval to send sip registration requests. Value is in seconds</param>
|
|||
|
/// <param name="bufferMiliseconds">Miliseconds for the buffer that stores the received voice packets</param>
|
|||
|
/// <param name="requestTimeout">Number of ms to wait before the sip request times out</param>
|
|||
|
/// <param name="socketIOport">port number use for socket.IO</param>
|
|||
|
/// <param name="localIPAddress">Local Ip adress. If not specified, the class will search for a local ip on the same network with the sip server ip</param>
|
|||
|
/// <param name="sendArsOnOff">True to send ars on on creation and off when calling Stop()</param>
|
|||
|
public SipClientClassSD(string sipDomain, int sipDomainPort, int localSipPort, string userName, string password, int registrationInterval, int bufferMiliseconds,
|
|||
|
int requestTimeout, int socketIOport, string localIPAddress = null, bool sendArsOnOff = true) :
|
|||
|
base(sipDomain, sipDomainPort, localSipPort, userName, password, registrationInterval, bufferMiliseconds, requestTimeout, localIPAddress)
|
|||
|
{
|
|||
|
|
|||
|
|
|||
|
// Socket IO
|
|||
|
this._socketIOClass = new SocketIOClass($"ws://{sipDomain}:{socketIOport}");
|
|||
|
this._socketIOClass.PrivateCallRequestReceived += _socketIOClass_PrivateCallRequestReceived;
|
|||
|
this._socketIOClass.GroupCallRequestReceived += _socketIOClass_GroupCallRequestReceived;
|
|||
|
this._socketIOClass.SmsReceived += _socketIOClass_SmsReceived;
|
|||
|
|
|||
|
// Send ARS ON after 1 second since the socketIOClass instantiation
|
|||
|
_sendArsOnOff = sendArsOnOff;
|
|||
|
if (_sendArsOnOff)
|
|||
|
Task.Delay(1000).ContinueWith(t => { this._socketIOClass.SendArs(new ArsInfo(true, "0", userName)); });
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#region Public Members
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets a value indicating if the Asterisk sends sms confirmations on delivery
|
|||
|
/// <para>Default value is false</para>
|
|||
|
/// </summary>
|
|||
|
public bool SmsConfirmationFromAsterisk
|
|||
|
{
|
|||
|
get { return base._smsConfirmationFromServer; }
|
|||
|
set { _smsConfirmationFromServer = true; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Send Invite to a Linx android Device
|
|||
|
/// </summary>
|
|||
|
/// <param name="linxID">Linx ID to call</param>
|
|||
|
public void Invite(string linxID)
|
|||
|
{
|
|||
|
// Send private call request on socket.IO
|
|||
|
PrivateCallInfo callInfo = new PrivateCallInfo(int.Parse(this.UserName), int.Parse(linxID), true);
|
|||
|
_linxDevicesToCall[linxID] = callInfo;
|
|||
|
_socketIOClass.SendPrivateCallRequest(callInfo);
|
|||
|
// send invite
|
|||
|
base.Invite(linxID, TypeOfCall.FULL_DUPLEX);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Send Invite to a linx group, using socket.IO signalling
|
|||
|
/// </summary>
|
|||
|
/// <param name="linxGroupID">Linx group to call</param>
|
|||
|
public new void InviteGroup(string linxGroupID)
|
|||
|
{
|
|||
|
// Send group call request on socket.IO
|
|||
|
GroupCallInfo callInfo = new GroupCallInfo(int.Parse(this.UserName), int.Parse(linxGroupID), true);
|
|||
|
_linxDevicesToCall[linxGroupID] = callInfo;
|
|||
|
_socketIOClass.SendGroupCallRequest(callInfo);
|
|||
|
// send invite
|
|||
|
base.Invite(linxGroupID, TypeOfCall.FULL_DUPLEX);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Method used to send Gps Request to a Linx device
|
|||
|
/// </summary>
|
|||
|
/// <param name="idToRequestGps">The sip id to send the gps request</param>
|
|||
|
/// <param name="seqID">The sequence id</param>
|
|||
|
public void SendGPSRequest(string idToRequestGps, string seqID)
|
|||
|
{
|
|||
|
base.SendLinxGpsRequest(idToRequestGps, seqID);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Sends a group sms on socket IO
|
|||
|
/// </summary>
|
|||
|
/// <param name="fromScID">SC ID of the sender</param>
|
|||
|
/// <param name="sipGroupID">The sip group ID</param>
|
|||
|
/// <param name="groupScID">SC ID of the group</param>
|
|||
|
/// <param name="text">The text</param>
|
|||
|
public void SendGroupSms(int fromScID, int sipGroupID, int groupScID, string text)
|
|||
|
{
|
|||
|
_socketIOClass.SendSms(new SmsInfo(_rand.Next(300, 1000).ToString(), UserName, fromScID.ToString(), sipGroupID.ToString(), groupScID.ToString(), text, true));
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Abstract Members Implementation
|
|||
|
|
|||
|
internal override RTPListener2 CreateRTPListener(UdpClient udpClient, bool initiatedByMe, int bufferMiliseconds, int sipIdInDialogWith, bool isGroupCall, TypeOfCall typeOfCall)
|
|||
|
{
|
|||
|
// Check if is private or group call
|
|||
|
CallInfo callInfo = null;
|
|||
|
string groupID = null;
|
|||
|
if (_linxDevicesToRespondTo.ContainsKey(sipIdInDialogWith.ToString()))
|
|||
|
callInfo = _linxDevicesToRespondTo[sipIdInDialogWith.ToString()];
|
|||
|
else if (_linxDevicesToCall.ContainsKey(sipIdInDialogWith.ToString()))
|
|||
|
callInfo = _linxDevicesToCall[sipIdInDialogWith.ToString()];
|
|||
|
if (callInfo is GroupCallInfo)
|
|||
|
{
|
|||
|
groupID = ((GroupCallInfo)callInfo).ToGroupSipID.ToString();
|
|||
|
}
|
|||
|
|
|||
|
return new RTPListenerLinx(udpClient, initiatedByMe, bufferMiliseconds, sipIdInDialogWith, typeOfCall, groupID);
|
|||
|
}
|
|||
|
|
|||
|
internal override RTPSender2 CreateRTPSender(UdpClient udpClient, int audioBitrate, IPEndPoint iPEndPoint, int dmrSourceID, int dmrDestinationID, bool initiatedByMe, TypeOfCall typeOfCall,
|
|||
|
RtpCallType halfDuplexCallType = RtpCallType.Private)
|
|||
|
{
|
|||
|
return new RTPSender2(udpClient, audioBitrate, iPEndPoint, dmrSourceID, dmrDestinationID, initiatedByMe, typeOfCall);
|
|||
|
}
|
|||
|
|
|||
|
internal override SipMessageGenerator SipMessageGenerator
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return _sipMessageGenerator;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Virtual Members Implementation
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Method used to return the type of call from a received invite
|
|||
|
/// </summary>
|
|||
|
/// <param name="receivedInvite">The received invite</param>
|
|||
|
/// <returns>The requested type of call</returns>
|
|||
|
protected override TypeOfCall GetTypeOfCall(Request receivedInvite)
|
|||
|
{
|
|||
|
return TypeOfCall.FULL_DUPLEX;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Method used to return the type of call from a received Session description
|
|||
|
/// </summary>
|
|||
|
/// <param name="sdp">The received session description</param>
|
|||
|
/// <returns>The requested type of call</returns>
|
|||
|
protected override TypeOfCall GetTypeOfCall(SessionDescription sdp)
|
|||
|
{
|
|||
|
return TypeOfCall.FULL_DUPLEX;
|
|||
|
}
|
|||
|
|
|||
|
/* Part that handles Sip message signalling
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Function that processes all the received Sip Message requests
|
|||
|
/// </summary>
|
|||
|
/// <param name="sipMessageRequest">The Sip Message request</param>
|
|||
|
/// <param name="senderID">The ID of the sender</param>
|
|||
|
protected override void ProcessReceivedSipMessage(Request sipMessageRequest, string senderID)
|
|||
|
{
|
|||
|
string requestBody;
|
|||
|
if ((requestBody = sipMessageRequest.Body) != null)
|
|||
|
{
|
|||
|
if (requestBody == _halfDuplexIdentifier)
|
|||
|
{
|
|||
|
lock (_locker)
|
|||
|
{
|
|||
|
_linxDevicesToRespondTo.Add(senderID);
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
base.ProcessReceivedSipMessage(sipMessageRequest, senderID);
|
|||
|
}
|
|||
|
|
|||
|
*/
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Method used to decide if the app is ringing or is sending Busy response to sender
|
|||
|
/// <para>Default behaviour is to ring (sends Ringing response)</para>
|
|||
|
/// </summary>
|
|||
|
/// <param name="receivedInvite">The received invite</param>
|
|||
|
/// <param name="senderSipId">The sender of the invite</param>
|
|||
|
/// <returns>True to send Ringing response, false to send Busy Here</returns>
|
|||
|
protected override bool SendResponseRinging(Request receivedInvite, string senderSipId)
|
|||
|
{
|
|||
|
// 1. Check if the caller is the one that has send / received the sip message. Else send busy
|
|||
|
lock (_lockerCallingLinx)
|
|||
|
{
|
|||
|
if (_linxDevicesToRespondTo.ContainsKey(senderSipId))
|
|||
|
{
|
|||
|
CallInfo callInfo = _linxDevicesToRespondTo[senderSipId];
|
|||
|
if (callInfo.FromSipID.ToString() == senderSipId)
|
|||
|
{
|
|||
|
if (callInfo.IsPTT)
|
|||
|
{
|
|||
|
GroupCallInfo groupCallInfo = callInfo as GroupCallInfo;
|
|||
|
if (groupCallInfo == null)
|
|||
|
{
|
|||
|
// this is a private call request
|
|||
|
return true;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// call is group call
|
|||
|
// reject a group call if already in dialog with that group
|
|||
|
bool reject = false;
|
|||
|
lock (_lockerlinxGroups)
|
|||
|
{
|
|||
|
if (!_linxGroupsInDialogWith.Contains(groupCallInfo.ToGroupSipID))
|
|||
|
_linxGroupsInDialogWith.Add(groupCallInfo.ToGroupSipID);
|
|||
|
else
|
|||
|
reject = true;
|
|||
|
}
|
|||
|
if (reject)
|
|||
|
{
|
|||
|
OnError(new ErrorEventArgs(UserName, senderSipId, $"Will reject group invite from {senderSipId} because I'm already in group call with {groupCallInfo.ToGroupSipID} "));
|
|||
|
return false;
|
|||
|
}
|
|||
|
else
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
#if DEBUG
|
|||
|
Console.WriteLine($"Will send busy to {senderSipId} because he initiated a full duplex call");
|
|||
|
#endif
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
#if DEBUG
|
|||
|
Console.WriteLine($"Will send busy to {senderSipId} because he did not send request for ptt");
|
|||
|
#endif
|
|||
|
OnError(new ErrorEventArgs(UserName, senderSipId, $"{senderSipId} did not send request for ptt"));
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
#if DEBUG
|
|||
|
Console.WriteLine($"Will send busy to {senderSipId} because he did not send request for ptt");
|
|||
|
#endif
|
|||
|
OnError(new ErrorEventArgs(UserName, senderSipId, $"{senderSipId} did not send request for ptt"));
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// This is the method that raises the DialogClosed event
|
|||
|
/// </summary>
|
|||
|
/// <param name="e">Data for the DialogClosed event</param>
|
|||
|
/// <remarks>Do not forget to call the base version when overrinding,
|
|||
|
/// <para>otherwize the DialogClosed Event will not fire</para>
|
|||
|
/// </remarks>
|
|||
|
protected override void OnDialogClosed(DialogClosedEventArgs e)
|
|||
|
{
|
|||
|
CallInfo closingCallInfo = null;
|
|||
|
if (_linxDevicesToCall.ContainsKey(e.SipIDinDialogWith))
|
|||
|
closingCallInfo = _linxDevicesToCall[e.SipIDinDialogWith];
|
|||
|
else if (_linxDevicesToRespondTo.ContainsKey(e.SipIDinDialogWith))
|
|||
|
closingCallInfo = _linxDevicesToRespondTo[e.SipIDinDialogWith];
|
|||
|
|
|||
|
string idInHangtime = "";
|
|||
|
GroupCallInfo groupCallInfo = closingCallInfo as GroupCallInfo;
|
|||
|
if (groupCallInfo != null)
|
|||
|
{
|
|||
|
// group call
|
|||
|
idInHangtime = groupCallInfo.ToGroupSipID.ToString();
|
|||
|
// Remove entry from linx groups dictionary
|
|||
|
lock (_lockerlinxGroups)
|
|||
|
{
|
|||
|
_linxGroupsInDialogWith.Remove(groupCallInfo.ToGroupSipID);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// private call
|
|||
|
idInHangtime = e.SipIDinDialogWith;
|
|||
|
}
|
|||
|
|
|||
|
// Remove sip id from list of id's that sent request on socket.IO
|
|||
|
lock (_lockerCallingLinx)
|
|||
|
{
|
|||
|
if (_linxDevicesToRespondTo.ContainsKey(e.SipIDinDialogWith))
|
|||
|
_linxDevicesToRespondTo.Remove(e.SipIDinDialogWith);
|
|||
|
else if (_linxDevicesToCall.ContainsKey(e.SipIDinDialogWith))
|
|||
|
_linxDevicesToCall.Remove(e.SipIDinDialogWith);
|
|||
|
}
|
|||
|
|
|||
|
// Create hangtime timer for each sip id
|
|||
|
TimerWithTag<LinxIDInfo> timer;
|
|||
|
if (_hangTimeTimers.ContainsKey(idInHangtime))
|
|||
|
{
|
|||
|
timer = _hangTimeTimers[idInHangtime];
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
timer = new TimerWithTag<LinxIDInfo>(_hangTimeDuration, new LinxIDInfo(e.SipIDinDialogWith, idInHangtime != e.SipIDinDialogWith ? idInHangtime : null));
|
|||
|
timer.AutoReset = false;
|
|||
|
timer.Elapsed += TimerHangtime_Elapsed;
|
|||
|
_hangTimeTimers.Add(idInHangtime, timer);
|
|||
|
}
|
|||
|
timer.Start();
|
|||
|
|
|||
|
LinxDialogClosedEventArgs ev = new LinxDialogClosedEventArgs(
|
|||
|
e.SipIDinDialogWith, e.SipIDwhoClosed, e.Reason,
|
|||
|
idInHangtime != e.SipIDinDialogWith ? idInHangtime : null);
|
|||
|
|
|||
|
this.OnLinxDialogClosed(ev);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// This is the method that raises the DialogCreated event
|
|||
|
/// </summary>
|
|||
|
/// <param name="e">Data for the DialogCreatedEvent</param>
|
|||
|
/// <remarks>Do not forget to call the base version when overrinding,
|
|||
|
/// <para>otherwize the DialogCreated Event will not fire</para>
|
|||
|
/// </remarks>
|
|||
|
protected override void OnDialogCreated(DialogCreatedEventArgs e)
|
|||
|
{
|
|||
|
|
|||
|
string linxID = (e.FromID == base.UserName ? e.ToID : e.FromID);
|
|||
|
bool isGroupCall = false;
|
|||
|
string groupID = null;
|
|||
|
CallInfo callInfo = null;
|
|||
|
|
|||
|
if (_linxDevicesToRespondTo.ContainsKey(linxID))
|
|||
|
callInfo = _linxDevicesToRespondTo[linxID];
|
|||
|
else if (_linxDevicesToCall.ContainsKey(linxID))
|
|||
|
callInfo = _linxDevicesToCall[linxID];
|
|||
|
|
|||
|
// Check if is private or group call
|
|||
|
if (callInfo is GroupCallInfo)
|
|||
|
{
|
|||
|
isGroupCall = true;
|
|||
|
linxID = groupID = ((GroupCallInfo)callInfo).ToGroupSipID.ToString();
|
|||
|
}
|
|||
|
|
|||
|
// Check if the Linx was in in hangtime, to stop the timer
|
|||
|
if (_hangTimeTimers.ContainsKey(linxID))
|
|||
|
{
|
|||
|
_hangTimeTimers[linxID].Stop();
|
|||
|
}
|
|||
|
|
|||
|
LinxDialogCreatedEventArgs ev = new LinxDialogCreatedEventArgs(
|
|||
|
e.ToID, e.FromID, e.TypeOfDialog, isGroupCall, groupID);
|
|||
|
|
|||
|
this.OnLinxDialogCreated(ev);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// This is the method that raises the VoiceReceived event
|
|||
|
/// </summary>
|
|||
|
/// <param name="e">Data for the VoiceReceived event</param>
|
|||
|
/// <remarks>Do not forget to call the base version when overrinding,
|
|||
|
/// <para>otherwise the VoiceReceived Event will not fire</para>
|
|||
|
/// </remarks>
|
|||
|
protected override void OnVoiceReceived(AudioEventArgs e)
|
|||
|
{
|
|||
|
LinxAudioEventArgs ev = (LinxAudioEventArgs)e;
|
|||
|
OnLinxVoiceReceived(ev);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Stops registration by sending unregister request to the sip server
|
|||
|
/// <para>Releases all the used resources</para>
|
|||
|
/// <para>Disconnects from the socket.IO server</para>
|
|||
|
/// </summary>
|
|||
|
public override void Stop(bool async = true)
|
|||
|
{
|
|||
|
// Send ARS OFF and disconect from socket IO
|
|||
|
if (_sendArsOnOff)
|
|||
|
_socketIOClass.SendArs(new ArsInfo(false, "0", UserName));
|
|||
|
_socketIOClass.Disconect();
|
|||
|
base.Stop();
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Events
|
|||
|
|
|||
|
private object locker = new object();
|
|||
|
/// <summary>
|
|||
|
/// Occurs when a GPS report is received from a Linx device
|
|||
|
/// </summary>
|
|||
|
public event EventHandler<LinxGpsDataEventArgs> GpsReportReceived
|
|||
|
{
|
|||
|
add
|
|||
|
{
|
|||
|
lock (locker)
|
|||
|
{
|
|||
|
base.LinxGpsReportReceived += value;
|
|||
|
}
|
|||
|
}
|
|||
|
remove
|
|||
|
{
|
|||
|
lock (locker)
|
|||
|
{
|
|||
|
base.LinxGpsReportReceived -= value;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private object locker1 = new object();
|
|||
|
/// <summary>
|
|||
|
/// Occurs when an emergency alarm is received from a Linx device
|
|||
|
/// </summary>
|
|||
|
public event EventHandler<LinxEmergencyAlarmReceivedEventArgs> EmergencyAlarmReceived
|
|||
|
{
|
|||
|
add
|
|||
|
{
|
|||
|
lock (locker1)
|
|||
|
{
|
|||
|
base.LinxEmergencyAlarmReceived += value;
|
|||
|
}
|
|||
|
}
|
|||
|
remove
|
|||
|
{
|
|||
|
lock (locker1)
|
|||
|
{
|
|||
|
base.LinxEmergencyAlarmReceived -= value;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private object locker8 = new object();
|
|||
|
/// <summary>
|
|||
|
/// Occurs when an Ars command is received from a Linx device
|
|||
|
/// </summary>
|
|||
|
public event EventHandler<Linx.ArsReceivedEventArgs> ArsReceived
|
|||
|
{
|
|||
|
add
|
|||
|
{
|
|||
|
lock (locker8)
|
|||
|
{
|
|||
|
base.LinxArsReceived += value;
|
|||
|
}
|
|||
|
}
|
|||
|
remove
|
|||
|
{
|
|||
|
lock (locker8)
|
|||
|
{
|
|||
|
base.LinxArsReceived -= value;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Occurs when hangtime is ended
|
|||
|
/// </summary>
|
|||
|
public event EventHandler<HangtimeEndedEventArgs> HangtimeEnded;
|
|||
|
|
|||
|
private void OnHangtimeEnded(HangtimeEndedEventArgs e)
|
|||
|
{
|
|||
|
HangtimeEnded?.Invoke(this, e);
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Occurs when a voice session (a dialog) has been established
|
|||
|
/// </summary>
|
|||
|
public new event EventHandler<LinxDialogCreatedEventArgs> DialogCreated;
|
|||
|
|
|||
|
private void OnLinxDialogCreated(LinxDialogCreatedEventArgs e)
|
|||
|
{
|
|||
|
DialogCreated?.Invoke(this, e);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Occurs when a voice session (a dialog) is closed
|
|||
|
/// </summary>
|
|||
|
public new event EventHandler<LinxDialogClosedEventArgs> DialogClosed;
|
|||
|
|
|||
|
private void OnLinxDialogClosed(LinxDialogClosedEventArgs e)
|
|||
|
{
|
|||
|
DialogClosed?.Invoke(this, e);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Ocurs when a voice buffer is received from a sip id
|
|||
|
/// </summary>
|
|||
|
public new event EventHandler<LinxAudioEventArgs> VoiceReceived;
|
|||
|
|
|||
|
private void OnLinxVoiceReceived(LinxAudioEventArgs e)
|
|||
|
{
|
|||
|
VoiceReceived?.Invoke(this, e);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Occurs when a group sms is received on socketIO
|
|||
|
/// </summary>
|
|||
|
public event EventHandler<SocketIOSmsEventArgs> GroupSmsReceived;
|
|||
|
|
|||
|
private void OnGroupSmsReceived(SocketIOSmsEventArgs e)
|
|||
|
{
|
|||
|
// New pattern for triggering events
|
|||
|
GroupSmsReceived?.Invoke(this, e);
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Private Methods
|
|||
|
|
|||
|
private void TimerHangtime_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
|||
|
{
|
|||
|
TimerWithTag<LinxIDInfo> timer = (TimerWithTag<LinxIDInfo>)sender;
|
|||
|
LinxIDInfo linxIDifo = timer.Tag;
|
|||
|
OnHangtimeEnded(new HangtimeEndedEventArgs(linxIDifo.SipID, linxIDifo.GroupID));
|
|||
|
}
|
|||
|
|
|||
|
#region SocketIO Event Handlers
|
|||
|
|
|||
|
private void _socketIOClass_PrivateCallRequestReceived(object sender, PrivateCallRequestReceivedEventArgs e)
|
|||
|
{
|
|||
|
PrivateCallInfo callInfo = e.CallInfo;
|
|||
|
if (callInfo.ToSipID.ToString() == this.UserName && callInfo.IsPTT)
|
|||
|
{
|
|||
|
lock (_lockerCallingLinx)
|
|||
|
{
|
|||
|
_linxDevicesToRespondTo[callInfo.FromSipID.ToString()] = callInfo;
|
|||
|
}
|
|||
|
}
|
|||
|
#if DEBUG
|
|||
|
Console.WriteLine($"Private call request received by sip {UserName}: {e.CallInfo.ToString()}");
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
private void _socketIOClass_GroupCallRequestReceived(object sender, GroupCallRequestReceivedEventArgs e)
|
|||
|
{
|
|||
|
GroupCallInfo callInfo = e.CallInfo;
|
|||
|
if (callInfo.IsPTT)
|
|||
|
{
|
|||
|
lock (_lockerCallingLinx)
|
|||
|
{
|
|||
|
_linxDevicesToRespondTo[callInfo.FromSipID.ToString()] = callInfo;
|
|||
|
}
|
|||
|
}
|
|||
|
#if DEBUG
|
|||
|
Console.WriteLine($"Group call request received by sip {UserName}: {e.CallInfo.ToString()}");
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
private void _socketIOClass_SmsReceived(object sender, SmsReceivedEventArgs e)
|
|||
|
{
|
|||
|
SmsInfo smsInfo = e.SmsInfo;
|
|||
|
#if DEBUG
|
|||
|
Console.WriteLine($"Group sms received by sip {UserName}: {smsInfo}");
|
|||
|
#endif
|
|||
|
if(smsInfo.IsGroup)
|
|||
|
// Fire event
|
|||
|
OnGroupSmsReceived(new SocketIOSmsEventArgs(smsInfo.SeqID, smsInfo.FromSipID, smsInfo.ToSipID, smsInfo.Message, smsInfo.IsGroup));
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
|
|||
|
struct LinxIDInfo
|
|||
|
{
|
|||
|
public string SipID { get; private set; }
|
|||
|
public bool IsGroup {
|
|||
|
get {
|
|||
|
return GroupID != null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public string GroupID { get; private set; }
|
|||
|
|
|||
|
|
|||
|
public LinxIDInfo(string sipID, string groupID = null)
|
|||
|
{
|
|||
|
SipID = sipID;
|
|||
|
GroupID = groupID;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Provides data for the DialogCreated event
|
|||
|
/// </summary>
|
|||
|
public class LinxDialogCreatedEventArgs : DialogCreatedEventArgs
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Gets the group sip id if group call, otherwise is null
|
|||
|
/// </summary>
|
|||
|
public string GroupID { get; private set; }
|
|||
|
internal LinxDialogCreatedEventArgs(string toID, string fromID, TypeOfCall typeOfDialog, bool isGroupCall, string groupID = null):
|
|||
|
base(toID, fromID, typeOfDialog, isGroupCall)
|
|||
|
{
|
|||
|
GroupID = groupID;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Provides data for the DialogClosedEvent
|
|||
|
/// </summary>
|
|||
|
public class LinxDialogClosedEventArgs : DialogClosedEventArgs
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Gets the group id if group call, otherwise is null
|
|||
|
/// </summary>
|
|||
|
public string GroupID { get; private set; }
|
|||
|
|
|||
|
internal LinxDialogClosedEventArgs(string sipIDinDialogWith, string sipIDWhoClosed, DialogClosedReason reason, string groupID = null) :
|
|||
|
base(sipIDinDialogWith, sipIDWhoClosed, groupID != null, reason)
|
|||
|
{
|
|||
|
GroupID = groupID;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Provides data for the HangtimeEnded Event
|
|||
|
/// </summary>
|
|||
|
public class HangtimeEndedEventArgs : SipEventArgs
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Returns true for group call, false for private call
|
|||
|
/// </summary>
|
|||
|
public bool IsGroupCall {
|
|||
|
get {
|
|||
|
return GroupID != null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets the group id if group call, otherwise is null
|
|||
|
/// </summary>
|
|||
|
public string GroupID { get; private set; }
|
|||
|
|
|||
|
internal HangtimeEndedEventArgs(string sipIDinDialogWith, string groupID = null) : base(sipIDinDialogWith)
|
|||
|
{
|
|||
|
GroupID = groupID;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Provides data for the VoiceReceived event
|
|||
|
/// </summary>
|
|||
|
public class LinxAudioEventArgs : AudioEventArgs
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Gets the group id if group call, otherwise is null
|
|||
|
/// </summary>
|
|||
|
public string GroupID { get; private set; }
|
|||
|
internal LinxAudioEventArgs(byte[] data, int linxSourceId, string groupID = null) :
|
|||
|
base(data, linxSourceId,
|
|||
|
groupID != null ? int.Parse(groupID) : linxSourceId)
|
|||
|
{
|
|||
|
GroupID = groupID;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Provides data for the GroupSmsReceived event
|
|||
|
/// </summary>
|
|||
|
public class SocketIOSmsEventArgs : EventArgs
|
|||
|
{
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// SequenceID
|
|||
|
/// </summary>
|
|||
|
public string SeqID { get; private set; }
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Sip ID that sends the sms
|
|||
|
/// </summary>
|
|||
|
public string FromSipID { get; private set; }
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Destination group sip ID
|
|||
|
/// </summary>
|
|||
|
public string ToSipID { get; private set; }
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The message
|
|||
|
/// </summary>
|
|||
|
public string Message { get; private set; }
|
|||
|
|
|||
|
public bool IsGroup { get; private set; }
|
|||
|
|
|||
|
internal SocketIOSmsEventArgs(string seqID, string fromSipID, string toSipID, string message, bool isGroup)
|
|||
|
{
|
|||
|
this.SeqID = seqID;
|
|||
|
this.FromSipID = fromSipID;
|
|||
|
this.ToSipID = toSipID;
|
|||
|
this.Message = message;
|
|||
|
this.IsGroup = isGroup;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Provides data for the SocketIoSmsAckReceived event
|
|||
|
/// </summary>
|
|||
|
public class SocketIoSmsAckEventArgs : EventArgs
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Seq id of the sms
|
|||
|
/// </summary>
|
|||
|
public string SeqID { get; private set; }
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Sip ID for the ack
|
|||
|
/// </summary>
|
|||
|
public string AckBySipID { get; private set; }
|
|||
|
|
|||
|
internal SocketIoSmsAckEventArgs(string seqID, string ackBySipID)
|
|||
|
{
|
|||
|
this.SeqID = seqID;
|
|||
|
this.AckBySipID = ackBySipID;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
}
|