Closed senpark15 closed 8 years ago
Yep, you can use one inbound socket to control many calls - once subscribed you will receive events for all channels on the switch.
I've had a think about this and here's one way you can do it. I've seen a pattern where you originate a call via an inbound socket and then hand off to an outbound socket listener to do further processing when the call has been answered.
The .Originate()
method wraps up the underlying events and completes the returned Task<OriginateResult>
when the call has been answered. You probably don't want to do this as you want to originate calls as quickly as the InboundSocket/FreeSwitch can handle it rather than blocking and waiting for the result of the call, so we'll drop down a level to the BgApi()
method and then hand off to our outbound listener. We'll handle failure scenarios in the Inbound Socket code:
using (var socket = await InboundSocket.Connect("localhost", 8021, "ClueCon"))
{
await socket.SubscribeEvents(EventName.BackgroundJob, EventName.ChannelHangup);
List<String> mobileNos = new List<String>({"8940703144", "8754006462"});
Dictionary<String, HangupCause> failures = new List<String>();
//we'll listen out for hangups that aren't NORMAL_CLEARING
//this will be for calls that return rejections, busy codes, failures
socket.Events.Where(x => x.EventName == EventName.ChannelHangup
&& x.HangupCause != HangupCause.NormalClearing)
.Subscribe(x =>
{
//i'm not sure which part of the event message will contain the info you want
//it will be in the headers or the variables
var dialledNumber = x.GetVariable("variable_dialed_user");
failures.Add(dialledNumber, x.HangupCause);
//log and deal with failure as appropriate
}
foreach(var item in mobileNos)
{
var bgJobResult = await socket.BackgroundJob(
"originate "
+ originateOptions.ToString()
+ "sofia/internal/"
+ item
+"@123.456.789 &socket(127.0.0.1:8084 async)");
if (!bgJobResult.Success)
{
//BgApi returns immediately if unable to dial, eg, incorrectly formatted dial string
//Log and handle errors as appropriate
}
}
Note that we originated a call and then handed over to the application socket(127.0.0.1:8084 async)
You can use an outbound listener to handle calls that were successfully answered. This way, the outbound listener part of the code onlu deals with calls that have been successfully set up and the inbound socket part of the code is only concerned with setting up calls, and will do so as fast as it can pump messages into FreeSwitch.
If you are doing any bridging at this point, you will need to use socket(127.0.0.1:8084 async full)
. A side effect of doing this is that you will receive events on the outbound socket for ALL channels in FreeSwitch, like an inbound socket. In order to reduce traffic, and remove the need to filter for specific events in your application code, you can have FreeSwitch do the filtering server-side.
await outboundSocket.Filter(HeaderNames.UniqueId, outboundSocket.ChannelData.UUID).ConfigureAwait(false); //filter for our unique id (in case using full socket mode)
await outboundSocket.Filter(HeaderNames.OtherLegUniqueId, outboundSocket.ChannelData.UUID).ConfigureAwait(false); //filter for channels bridging to our unique id
await outboundSocket.Filter(HeaderNames.ChannelCallUniqueId, outboundSocket.ChannelData.UUID); //filter for channels bridging to our unique id
This is how the Channel API works. Alternatively you could just use the Channel API which handles much of the complexity for you.
Alternatively, if you write your call handling logic in an XML dialplan originate destination &transfer(extension XML default)
, you can simply originate calls from the inbound socket and transfer to an extension in the dialplan which will handle your call logic - this will give you the best performance but you will be giving up the ability to write your call logic in C#.
..and I've just fixed a bug in .BackgroundJob()
eff8552150076a41895f41390a600fcf9d6536e8 if you are running off a fork please update to latest.
thank you very much for your response. I will try your suggestions and come back if i have any problem
I got latest code. I tried you suggestions. here is the code sample.
class Program
{
public static int _currentCallCount = 0;
static void Main(string[] args)
{
Task.Run(() => { CheckCallCount(); });
Task.Run(() => { MakeCall(); });
using (var listener = new OutboundListener(8084))
{
listener.Connections.Subscribe(
async socket =>
{
await socket.Connect();
var uuid = socket.ChannelData.Headers[HeaderNames.UniqueId];
Console.WriteLine("OutboundSocket connected for channel " + uuid);
await socket.Play(uuid, "misc/8000/misc-learn_more_about_freeswitch_solutions.wav");
await socket.Play(uuid, "misc/8000/misc-freeswitch_is_state_of_the_art.wav");
await socket.Hangup(uuid, HangupCause.NormalClearing);
});
listener.Start();
Console.WriteLine("Press [Enter] to exit.");
Console.ReadLine();
}
Console.ReadLine();
}
private static async Task CheckCallCount()
{
using (var socket = await InboundSocket.Connect("localhost", 8021, "ClueCon"))
{
while (true)
{
var res = await socket.SendApi("show calls count");
Console.WriteLine("Current Calls Count " + Convert.ToInt32(res.BodyText.Split(' ')[0]));
_currentCallCount = Convert.ToInt32(res.BodyText.Split(' ')[0]);
Thread.Sleep(2000);
}
}
}
private static async Task MakeCall()
{
using (var socket = await InboundSocket.Connect("localhost", 8021, "ClueCon"))
{
await socket.SubscribeEvents(EventName.ChannelHangup);
socket.Events.Where(x => x.EventName == EventName.ChannelHangup
&& x.HangupCause != HangupCause.NormalClearing)
.Subscribe(x =>
{
Console.WriteLine("Hangup Detected : " + x.GetVariable("mobile_no"));
});
List<String> mobileNos = null;
while (true)
{
// only four concurrent calls allowed
while (_currentCallCount == 4)
{
Thread.Sleep(5000);
}
// mobile no comes from database
mobileNos = new List<string>() { "8940703114", "8754006482" };
foreach (var mobileNo in mobileNos)
{
Console.WriteLine("Call initiating : " + mobileNo);
var originateOptions = new OriginateOptions
{
CallerIdName = "874561",
IgnoreEarlyMedia = true
};
var bgJobResult = await socket.BackgroundJob(
"originate "
+ originateOptions.ToString()
+ "sofia/internal/" + mobileNo + "@192.168.1.250 &socket(127.0.0.1:8084 async)");
if (!bgJobResult.Success)
{
Console.WriteLine("Call Failed to initiate : " + mobileNo);
}
else
{
Console.WriteLine("Call Successfully initiated : " + mobileNo);
}
}
Thread.Sleep(5000);
}
}
}
}
here are the issues.
please let me know what i am missing or doing wrong.
The BackgroundJob will only complete immediately if FreeSwitch cannot process the origination command - this will happen due to bad inputs/programming error. Otherwise it will await until the call is answered or fails.
OutboundListener play the first audio file and stay there
This is because we did not get a CHANNEL_EXECUTE_COMPLETE event.. the reason for this is below:
We need to wrap the origination application in single quotes: '&socket(127.0.0.1:8084 async full)'
If it is not wrapped in quotes FreeSwitch parses it as socket 127.0.0.1:8084
and misses out the arguments async full
.
I needed to use async full
because FS won't let me subscribe to events without it, which is annoying.
If i disconnect call from mobile , it throws task cancelled exception.
Yes this is expected behaviour - as mentioned in README you need to try { } catch(OperationCanceledException) { }
inside your Outbound listener subscription to handle the scenario where the caller hangs up in the middle of execution.
I will surface command and api errors as errors in logging and also adjust the originate command.
Please see sample code in https://github.com/danbarua/NEventSocket/commit/8e43b3b9db98c63e67d5cc762a30abe792f1c204
@senpark15 I have left some example code here: https://github.com/danbarua/NEventSocket/blob/master/src/NEventSocket.Examples/Examples/VoiceBlaster.cs
You might want to review the logic around rate-limiting etc. Please re-open this issue and continue the discussion if you have any problems.
I am developing IVR application. I want to make multiple concurrent calls through gsm gateway. should i create multiple inbound socket instances to make concurrent calls or can i make concurrent calls through single socket instance and play audio files based on call id? could you please give any sample code?
Here is my sample code for make one call at time