locked
Problems with supervised transfer sample RRS feed

  • Question

  • I've incorporated the SupervisedTransfer activity from the sample app into my application. However the transfers are failing. It is trying to transfer to the correct SIP extension but on the wrong device (i.e. the IP is incorrect).

     

    I set up the parms like this:

    sipAddress = "192.168.2.3";

    if (firstTime)

    {

    sipEndpoint = "3333";

    return "TRANSFER";

    //sipEndpoint = "2222";

    //return "EXECUTESCRIPT";

    }

    else

    return "DISCONNECT";

    }

     

     

    Then I call the supervised transfer activity like this:

    this.supervisedTransfer.CalledParty = sipEndpoint;

    this.supervisedTransfer.CallingParty = this.TelephonySession.CallInfo.CallingParty.Uri.User;

     

    Extension 3333 is a speech app  on the same server that I'm using for testing. It just answers the call, plays a message and disconnects.

     

    When my app tries to transfer to it I get a "I'm sorry, I can't connect your call. Please try again later." message.

     

    I get this error in the Event logs. If you look at the number shown below in bold you can see that the sip endpoint is correct but the server IP is the IP of the box that the original SIP call came from and not the IP of the Speech Server where the speech application I'm trying to transfer to resides.

     

    Am I missing something here? Maybe I don't understand this but I thought it should be trying to transfer to "sip:3333@localhost" or in other words the IP should be that of the Speech Server not the calling phone.

     

    Evnet ID: 29025

     

    Application Error 100: An error occurred during call transfer: Microsoft.SpeechServer.TelephonyException: A SIP request has failed, but no response code was received. The current operation is 'Opening'. The session state is 'Connecting'. The remote participant is 'sip:3333@192.168.2.8:5060;user=phone'. ---> Microsoft.Rtc.Signaling.ConnectionFailureException: Unable to establish a connection.

    at Microsoft.Rtc.Signaling.SipAsyncResult.ThrowIfFailed()

    at Microsoft.Rtc.Signaling.Helper.EndAsyncOperation[T](Object owner, IAsyncResult asyncResult)

    at Microsoft.Rtc.Signaling.SignalingSession.EndSetConnection(IAsyncResult asyncResult)

    at Microsoft.Rtc.Signaling.SignalingSession.SetConnection(String proxyHost, Int32 port, SipTransportType transport)

    at Microsoft.SpeechServer.Core.TelephonySessionOutbound.CreateOutboundSignalingSession(SipUri toAddress, SipUri fromAddress, SipPeer sipPeer, Boolean isProxy)

    at Microsoft.SpeechServer.Core.TelephonySessionOutbound.AllocateEndpoint()

    at Microsoft.SpeechServer.Core.TelephonySessionOutbound.OnJitCreateSpeechSessionCompleted(Object sender, AsyncCompletedEventArgs e)

    --- End of inner exception stack trace ---

    For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

     

     

     

    Monday, August 13, 2007 7:02 PM

All replies

  •  

    If you look in SupervisedTransferActivity's implementation, in TelephonySession_HoldCompleted, CalledParty is passed as a string to OpenAsync.  This overload of OpenAsync treats CalledParty as a telephony number, and builds the SipUri around it based upon one of the Sip Peers configured for transfers - ie. exactly what you see.

     

    You will need to modify SupervisedTransferActivity to pass in a SipUri to OpenAsync.

    Wednesday, August 15, 2007 7:22 PM
  • Thanks Anthony,

     

    I've added two properties to the SupervisedTransfer class like this:

     

    Code Snippet

    public SipUri CalledPartySipUri

    {

    get

    {

    return (SipUri)GetValue(CalledPartySipUriProperty);

    }

    set

    {

    SetValue(CalledPartySipUriProperty, value);

    }

    }

    public SipUri CallingPartySipUri

    {

    get

    {

    return (SipUri)GetValue(CallingPartySipUriProperty);

    }

    set

    {

    SetValue(CallingPartySipUriProperty, value);

    }

    }

     

     

    I set these properties like this:

    Code Snippet

    this.supervisedTransfer.CalledPartySipUri = new SipUri(SipUriScheme.Sip, "3333", "192.168.2.8", 0, "transport=tcp");

    this.supervisedTransfer.CallingPartySipUri = this.TelephonySession.CallInfo.CallingParty.Uri;

     

     

     

    And then I make the connection using this code:

    Code Snippet

    _consultationSession.OpenAsync(CalledPartySipUri,CalledPartySipUri);

     

     

    The results are the same :

    Application Error 100: An error occurred during call transfer: Microsoft.SpeechServer.TelephonyException: A SIP request has failed, but no response code was received. The current operation is 'Opening'. The session state is 'Connecting'. The remote participant is 'sip:3333@192.168.2.8:5070;transport=tcp'. ---> Microsoft.Rtc.Signaling.ConnectionFailureException: Unable to establish a connection.

    at Microsoft.Rtc.Signaling.SipAsyncResult.ThrowIfFailed()

    at Microsoft.Rtc.Signaling.Helper.EndAsyncOperation[T](Object owner, IAsyncResult asyncResult)

    at Microsoft.Rtc.Signaling.SignalingSession.EndSetConnection(IAsyncResult asyncResult)

    at Microsoft.Rtc.Signaling.SignalingSession.SetConnection(String proxyHost, Int32 port, SipTransportType transport)

    at Microsoft.SpeechServer.Core.TelephonySessionOutbound.CreateOutboundSignalingSession(SipUri toAddress, SipUri fromAddress, SipPeer sipPeer, Boolean isProxy)

    at Microsoft.SpeechServer.Core.TelephonySessionOutbound.AllocateEndpoint()

    at Microsoft.SpeechServer.Core.TelephonySessionOutbound.OnJitCreateSpeechSessionCompleted(Object sender, AsyncCompletedEventArgs e)

    --- End of inner exception stack trace ---

    For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

     

    Can you explain how supervised transfers are supposed to work? It seems odd that the transfer is trying to use the IP of the calling party. But if I switch the IP string in the CalledPartySipUri to the IP address of the Speech Server (where the app resides then I get the following -

     

    Application Error 100: An error occurred during call transfer: System.InvalidOperationException: The SIP peer on which an outbound call is placed for a supervised transfer must match the SIP peer used on the original call. The original SIP peer address is '192.168.2.8' but the specified peer address is '192.168.2.3'.

     

    Is the Speech Server just tellling the calling party to redirect to the other app or what really happens in a supervised transfer?

     

    I'm using XLite as a SIP phone on machine AA (192.168.2.8) to call the application running on machine BB (192.168.2.3). The intention is for machine BB to transfer the call to an app with an endpoint of "3333" running on Mahine BB but that is failing.

     

     

    Monday, August 20, 2007 5:22 PM
  • Ah yes, this restriction is placed on outbound calls for supervised transfer.  For a supervised transfer, Speech Server establishes a connection to the recipient (the OpenAsync), then 'gives' this connection to the calling party (the TransferAsync), and Speech Server drops out.  The restriction is to ensure that the gateway can connect these 2 connections together, though in practice it may not be necessary.  Certainly in your case where the SIP Peer is a SIP phone rather than a gateway it is inappropriate. 

     

    One question - do you need to use SupervisedTransfer?  BlindTransfer should not have this restriction.  If your 3333 app always accepts the incoming call, then BlindTransfer should be sufficient (although under high load it could be rejected).  The only reason I can see for wanting SupervisedTransfer in this case is if your 3333 app has logic which may decline the call.

     

     

    Monday, August 20, 2007 6:44 PM
  • Without going into too much detail the answer is yes, I need supervised transfer. The client requires that all calls go through my application. In the case of the supervised transfer my app is supposed to report the status of the transfer before dropping out of the call.

     

    Any suggestions on how to achieve this?

     

    If I left the code alone would it work if connected to a gateway or PBX instead of just using a SIP phone? In other words is my test platform what is causing the problem? Would this work in a real environment?

     

    Monday, August 20, 2007 6:52 PM
  • In theory a gateway / PBX should be able to recognise the called party as one of it's own & route it back to Speech Server.  In practice, with the gateways that we tried, they sent it out to the PSTN; although it should then come back to the gateway, it'll use extra channels, so is decidedly sub-optimal.

     

    We did however manage to configure the Interaction Sip Proxy to route the call straight back to Speech Server.  The setup here is to have the proxy (configured as the trusted sip peer) between the Sip Phone (or gateway / PBX) and Speech Server.

    Monday, August 20, 2007 10:12 PM
  • Thanks for all of your help.

     

    Here is the sequence of SIP events that is taking place. The 2 INVITES from MSS to the phone have different sequence numbers then the call is terminated with an error.

     

    Phone ==> INVITE    ==> MSS
    MSS ==> 100 Trying    ==> Phone
    MSS ==> 302 Moved Temporarily  ==> Phone
    Phone  ==> ACK    ==> MSS
    Phone  ==> INVITE    ==> MSS
    MSS ==> 100 Trying    ==> Phone
    MSS ==> 180 Ringing   ==> Phone
    MSS ==> 200 OK    ==> Phone
    Phone  ==> ACK    ==> MSS
    --------------------------The SUpervised Transfer is initialed in the APP---------------------------
    MSS ==> INVITE    ==> Phone
    Phone  ==> 200 OK    ==> MSS
    MSS ==> ACK    ==> Phone
    MSS ==> INVITE    ==> Phone
    Phone  ==> 200 OK    ==> MSS
    MSS ==> ACK    ==> Phone
    MSS ==> BYE    ==> Phone
    Phone  ==> 200 OK    ==> MSS

     

    The error message I'm getting is from the TelephonySession_ResumeCompleted (the "I'm Sorry..." message). I don't understand why if there is no error that we are playing that message and closing the session. Can you explain that.

     

    Code Snippet

    private void TelephonySession_ResumeCompleted(object sender, AsyncCompletedEventArgs e)

    {

    _telephonySession.ResumeCompleted -= TelephonySession_ResumeCompleted;

    if (e.Error != null)

    {

    // If an error occurs on the resume of the original call - log the error

    _telephonySession.LoggingManager.LogApplicationError(100,

    "An unexpected error occurred on resuming the original call: {0}",

    e.Error.Message);

    }

    else

    {

    PromptBuilder builder = new PromptBuilder(System.Globalization.CultureInfo.CurrentCulture);

    builder.AddPromptDatabase(new Uri(Workflow.ApplicationHost.LocalPath, "Prompts/SupervisedTransferPrompts.prompts"));

    builder.SetText("I'm sorry, I can't connect your call. Please try again later.");

    _telephonySession.Synthesizer.SpeakCompleted += TelephonySession_SpeakCompleted;

    _telephonySession.Synthesizer.SpeakAsync(builder);

    }

    // Close the consultation session

    _consultationSession.Closed += ConsultationSession_Closed;

    _consultationSession.Close();

    }

     

     

     

     

    I don't understand why the application i shaving this problem.

     

    I can send ou the code for the app if it helps.

     

    Tuesday, August 21, 2007 2:27 PM
  • In the SupervisedTransfer sample app, TelephonySession_ResumeCompleted is raised upon completion of

    _telephonySession.ResumeAsync.  There are several places were this is called; if OpenAsync fails, if there is a reco error on the consultation call or if the callee doesn't respond 'yes'.  The latter two are app logic - replace this with your own. 

     

    Does that answer your question?

    Tuesday, August 21, 2007 9:56 PM
  • I'll check the OpenAsync to see if there is something there that I am missing.

     

    The other two places you mentioned don't apply as I have removed the question so the callee doesn't have anything to respond to.

     

    I really just want to control the call not give the callee the option of rejecting it - i.e. no whisper.

     

    Wednesday, August 22, 2007 1:08 AM
  • The _consultationSession.OpenAsync(CalledParty) is where the failure is occurring.

     

    In the ConsultationSession_OpenCompleted we are falling into this code

     

    // If the make call failed, save the error, be sure to close the consultation call,

    // and then try to resume the original call.

    if (e.Error != null)

    {

    _transferError = e.Error;

    _consultationSession.Closed += ConsultationSession_Closed;

    _consultationSession.Close();

    _telephonySession.ResumeCompleted += TelephonySession_ResumeCompleted;

    _telephonySession.ResumeAsync();

    }

     

    I'm just not sure why we are failing. In the SIP phone I see the call go on hold and then it just sets there untill the call fails.  In another SIP phone I see the call go on hold then the INVITE from MSS shows up as a call to the phone but I can't answer it and it just sets there until the call fails. Bear in mind that I'm not attempoting to transfer to the phone but rather to another MSS app.

     

    Can the SupervisedTransfer be made to work while using a soft phone that connects via a simple URI or is it something that will require some kind of gateway or PBX to assist in the routing?

     

    I have tried SipXphone and X-Lite for testing.

     

     

    Wednesday, August 22, 2007 1:40 AM
  • There is no reason why a sip-phone couldn't work, but I'd be surprised if there is one that would work, and it sounds lke you've identified 2 that don't work.

     

    Have you tried putting a SIP Proxy in between the sip phone and Speech Server?  As I said above, we have successfully configured Interaction Sip Proxy to handle this.

    Wednesday, August 22, 2007 4:29 AM
  • I've downloaded Interaction Sip Proxy.

     

    Any tips on installing and configuring it?

     

    Can it be installed on the speech server box without any problems or does it need to reside on another server so that there are no port conflicts?

     

     

    Wednesday, August 22, 2007 11:48 AM
  •  

    Hi Marshall,
    Have you found a way to make it work?

     

    I am trying to do a simple variant of supervisedtransfer, I just want it to connect the incoming call to a pbx without the user accepting the call to actually have to "accept". Like a blindtransfer but including the incoming callers number.

     

    I started out with the supervisedtransfer example and tried to remove all parts that had to do with accepting the call (see results below). Got the following error when I tried:
    The requested operation 'TransferAsync()' on the Telephony Session failed for the following reason: 'A transfer failed in call state Transferring after receiving notify message Failed.'.
    Further trace information for support personnel follows:
    Microsoft.SpeechServer.TelephonyException: A transfer failed in call state Transferring after receiving notify message Failed.

    Do you think this is related to the PBX/SIP setup or just due to errors in my code? I'm hoping for the latter...
    Blindtransfers work fine (besides that callers number isn't sent to the user that the call is directed to).


    Thanks for you input!
    / Markus

     

     

    Code Snippet

    public SuperTransferActivity(): base()
    {
    }

     

    protected override void ExecuteCore(ActivityExecutionContext executionContext)
    {
       if (String.IsNullOrEmpty(CalledParty))
       {
         throw new InvalidOperationException("SupervisedTransferActivity_CalledPartyNotSe(Name)");
       }

       

       _telephonySession = Workflow.TelephonySession;

       // Default CallingParty to the original party's number
       if (String.IsNullOrEmpty(CallingParty))
       {
          CallingParty = _telephonySession.CallInfo.CallingParty.Uri.User;
       }

       if (_telephonySession.State != TelephonySessionState.Connected)
       {
          throw new InvalidOperationException("TelephonyActivity_InvalidStateForExecute(_helper.ActivityType, Name, TelephonySessionState.Connected.ToString())");
       }

     

       //
       //This code is from TelephonySession_HoldCompleted which is not used.
       // Create the new session for the consultation call
       _consultationSession = _telephonySession.CreateSession(TelephonySessionType.SupervisedTransfer);
       _consultationSession.OpenCompleted += ConsultationSession_OpenCompleted;
       _consultationSession.OpenAsync(CalledParty, CallingParty);

       base.ExecuteCore(executionContext);
    }


    protected override void CancelCore(ActivityExecutionContext executionContext)
    {
       if (_telephonySession != null)
       {
          //_telephonySession.HoldCompleted += TelephonySession_HoldCompleted;
          _telephonySession.TransferCompleted -= TelephonySession_TransferCompleted;
          _telephonySession.ResumeCompleted -= TelephonySession_ResumeCompleted;
          if (_consultationSession != null)
          {
             _consultationSession.OpenCompleted +=  ConsultationSession_OpenCompleted;
          }
       }
       base.CancelCore(executionContext);
    }


    private void ConsultationSession_OpenCompleted(object sender, AsyncCompletedEventArgs e)
    {
       _consultationSession.OpenCompleted -= ConsultationSession_OpenCompleted;

       // If the make call failed, save the error, be sure to close the consultation call,
       // and then try to resume the original call.
       if (e.Error != null)
       {
          _transferError = e.Error;

          _consultationSession.Closed += ConsultationSession_Closed;
          _consultationSession.Close();

          _telephonySession.ResumeCompleted += TelephonySession_ResumeCompleted;
          _telephonySession.ResumeAsync();
       }
      {


          // complete the transfer
          _telephonySession.TransferCompleted += TelephonySession_TransferCompleted;
          _telephonySession.TransferAsync();
       }
    }


    private void TelephonySession_TransferCompleted(object sender, AsyncCompletedEventArgs e)
    {
       _telephonySession.TransferCompleted -= TelephonySession_TransferCompleted;
       this.Close(e.Error);
    }

     

    private void TelephonySession_ResumeCompleted(object sender, AsyncCompletedEventArgs e)
    {
       _telephonySession.ResumeCompleted -= TelephonySession_ResumeCompleted;

       if (e.Error != null)
       {
          //  If an error occurs on the resume of the original call - log the error
          _telephonySession.LoggingManager.LogApplicationError(100, 
          "An unexpected error occurred on resuming the original call: {0}", 
           e.Error.Message);
        }

     

       // Close the consultation session
       _consultationSession.Closed += ConsultationSession_Closed;
       _consultationSession.Close();

     

       //throw error so it is catched by main flow
       if (e.Error != null)
                    throw e.Error;
    }

     

    private void TelephonySession_SpeakCompleted(object sender, AsyncCompletedEventArgs e)
    {
       _telephonySession.Synthesizer.SpeakCompleted -= TelephonySession_SpeakCompleted;
     
       // Close the original telephony session
       _telephonySession.Closed += TelephonySession_Closed;
       _telephonySession.Close();
    }


    private void ConsultationSession_Closed(object sender, ClosedEventArgs e)
    {
       if (_transferError != null)
       {
          //  If a transfer error occured  - log the error
          _telephonySession.LoggingManager.LogApplicationError(100,
                        "An error occurred during call transfer: {0}", _transferError);
       }


       if (_consultationSession != null)
       {
          _consultationSession.Closed -= ConsultationSession_Closed;
       }
    }


    private void TelephonySession_Closed(object sender, ClosedEventArgs e)
    {
       if (_telephonySession != null)
       {
          _telephonySession.Closed -= TelephonySession_Closed;
       }

       this.Close(e.Error);
    }

     

     

    Wednesday, September 26, 2007 6:55 AM
  • Hi Markus,

     

    I was trying to do the same thing you are doing and I got the same errors. I removed the grammars and the prompting of the destination party and I'm pretty sure the code was correct. Still I couldn't get it to work. I think it may be a problem with the software SIP phone I was using but I'm not sure. If used with a gateway or PBX that properly recognized the transfer and handled it then it may work.

     

    Luckily for me the client changed the specs and I ended up doing a simple blind transfer so I have quit working on the problem.

     

    Let me know if you figure anything out.

     

    I have a SIP gateway that should arrive today so I may revisit this sometime soon. If I get it working I'll blog about it.

     

     

     

    Wednesday, September 26, 2007 11:46 AM