Bridge Communications

Saturday, November 14, 2015

Creating your first UCMA application - Part II

Last week we began this series on creating your first UCMA application.  We began with the Topology creation of the trusted application pool, trusted application, and trusted application endpoint.  This week we'll focus our attention to making our connection using the topology objects we created last week.

What better way to learn than to create a real app, and that is what I am going to layout for you here over the next several weeks.  What will our app do?  Our app is going to listen for incoming calls, answer them, ask the caller who they are looking for, and then transfer that caller.


Most of what we are going to do here is based off the samples provided with the UCMA sdk, frankly if not for them, I am not sure any of us would have ever figured out how to connect.  The process is multi-step so it is important to be able to log what is going on, or not working in our console app.  Let's create 2 classes, ConsoleLogger, and ILogger.

 public class ConsoleLogger : ILogger
    {
        public void Log(string message)
        {
            Console.WriteLine(message);
        }

        public void Log(string message, Exception ex)
        {
            Console.WriteLine(message, ex);
        }

        public void Log(string message, params object[] arg)
        {
            Console.WriteLine(message, arg);
        }
    }


public interface ILogger
    {
        void Log(string message);

        void Log(string message, Exception ex);

        void Log(string message, params object[] arg);
    }

These are staples of the example programs, and we'll use them in our application as well.  Let's also make use of their UCMASampleHelper class in our app.  I won't paste the entire thing in here, you can find it at the link or in the sample code.  We aren't going to use it as well, there is no need for us to key in data each time we'll code that into our app.

Let's make a class to store our people in.

public class classUsers
        {
            public classUsers(string name, string uri, string hear)
            {
                Name = name;
                Uri = uri;
                Hear = hear;
            }

            public string Name { get; private set; }
            public string Uri { get; private set; }
            public string Hear { get; private set; }
        }

We'll listen for both the Name and Hear items, but use the Name to tell the caller who we are transferring them to.

In my actual virtual operator I run a timer and pull in new folks from a SQL table that is front ended by a web interface, so we don't have to kill the program and re-compile each time a new user is added, but for this example we'll keep it simple.


Establishing the connection.

To begin let's declare a couple things at the top of our Program.cs

ILogger _logger = new ConsoleLogger();
        ProvisionedApplicationPlatformSettings settings;

        CollaborationPlatform _collaborationPlatform;
        ApplicationEndpoint _appEndpoint;
        int _endpointsDiscovered = 0;
        ManualResetEvent _startupWaitHandle = new ManualResetEvent(false);
        ManualResetEvent _shutdownWaitHandle = new ManualResetEvent(false);

        List<classUsers> _users = new List<classUsers>();

        private UCMASampleHelper _helper;

Then let's add this to our Main()

static void Main(string[] args)
        {
            Program myoperator = new Program();
            myoperator.Run();
        }

and finally let's look at our Run()

public void Run()
        {
            try
            {
                Console.BackgroundColor = ConsoleColor.White;
                Console.ForegroundColor = ConsoleColor.Red;
                Console.Clear();
                Console.Title = "Virtual Operator";

                Console.SetWindowSize(600, 400);

                Console.SetWindowPosition(100, 100);

                //Console.WindowLeft = 100;
                //Console.WindowTop = 75;

                //Console.WindowHeight = 400;
                //Console.WindowWidth = 600;

            }
            catch { }

            try
            {
                _users.Add(new classUsers("Your Full Name", "sip:you@yourdomain.com", "YourFirstName"));

            }
            catch { }

            string applicationUserAgent = "yourucmaappname";
            string applicationId = "urn:application:yourucmaappname";

            settings = new ProvisionedApplicationPlatformSettings(applicationUserAgent, applicationId);

            // Create a new collaboration platform with the settings.
            _collaborationPlatform = new CollaborationPlatform(settings);
            _collaborationPlatform.RegisterForApplicationEndpointSettings(OnApplicationEndpointSettingsDiscovered);

            Task.Factory.StartNew(() =>
            {
                try
                {
                    _helper = new UCMASampleHelper();
                }
                catch { }
            });


            // Start the platform as an asynchronous operation.
            _collaborationPlatform.BeginStartup(OnPlatformStartupCompleted, null);

            mTimer.Interval = 1000;
            mTimer.Elapsed += mTimer_Elapsed;
            mTimer.Start();

            // Pause the console to allow for easier viewing of logs.
            _logger.Log("Press any key to end the sample.");
            Console.ReadKey();
            ShutDownPlatform();


        }

So we get started by moving the window around a bit and changing the color.  From there we add a sample user to listen for into our _users object.

The strings applicationUserAgent and applicationID are used to get our apps settings from the sever and those are then returned to use in the settings objects.

The collaboration platform is then built based on those settings, and then we call RegisterForApplicationEndpointSettings(OnApplicationEndpointSettingsDiscovered); which basically begins the process once they are discovered.

The final thing of note is at the end we start the collaboration platform, then wait for for the user to press a key to stop it, essentially running the program forever until the user tells it to stop.

_collaborationPlatform.BeginStartup(OnPlatformStartupCompleted, null);

So what happens in these objects along the way?

 private void OnApplicationEndpointSettingsDiscovered(object sender, ApplicationEndpointSettingsDiscoveredEventArgs args)
        {
            // Keep track of how many endpoints we've found
            // so that we only take one.
            Interlocked.Increment(ref _endpointsDiscovered);

            if (_endpointsDiscovered > 1)
            {
                // We've already found an endpoint
                // and we don't need another one. Sorry!
                return;
            }

            args.ApplicationEndpointSettings.IsDefaultRoutingEndpoint = true;
            //args.ApplicationEndpointSettings.Presence.PreferredServiceCapabilities.ApplicationSharingSupport = CapabilitySupport.Supported;
            //args.ApplicationEndpointSettings.Presence.PreferredServiceCapabilities.InstantMessagingSupport = CapabilitySupport.Supported;
            args.ApplicationEndpointSettings.Presence.PreferredServiceCapabilities.AudioSupport = CapabilitySupport.Supported;


            _appEndpoint = new ApplicationEndpoint(_collaborationPlatform, args.ApplicationEndpointSettings);

            //try to get all calls


            _appEndpoint.BeginEstablish(OnApplicationEndpointEstablishCompleted, null);
        }


      public void WaitForStartup()
        {
            _startupWaitHandle.WaitOne();
        }

        private void OnPlatformStartupCompleted(IAsyncResult result)
        {
            try
            {
                // Finish the startup operation.
                _collaborationPlatform.EndStartup(result);

                HoldMusic.loadHold();

                _logger.Log("Collaboration platform started.");
            }
            catch (RealTimeException ex)
            {
                _logger.Log("Platform startup failed: {0}", ex);
            }
        }


You see once the settings are discovered we create the _appEndpoint.  This is shown in blue above.  We only want audio we don't want our app using application sharing or im, so those are commented out but left visible for reference.  As you would expect any errors are going to be displayed on the screen by the logger.

We will also log the state change of our app endpoint.

private void _appEndpoint_StateChanged(object sender, LocalEndpointStateChangedEventArgs e)
        {
            _logger.Log("Application endpoint state changed from {0} to {1}", e.PreviousState, e.State);
        }

It is helpful to see the process.  So our final look today is what we do when the app endpoint is successful, we are going to register for the incoming call events,

private void OnApplicationEndpointEstablishCompleted(IAsyncResult result)
        {
            try
            {

                _appEndpoint.EndEstablish(result);

                _logger.Log(_appEndpoint.EndpointUri);
                _logger.Log(_appEndpoint.OwnerPhoneUri);
                _logger.Log(_appEndpoint.OwnerUri);
                _logger.Log(_appEndpoint.OwnerDisplayName);

                _logger.Log("Application endpoint established.");

                _startupWaitHandle.Set();
               
                _logger.Log("Registering for Incoming Calls...");

                _appEndpoint.RegisterForIncomingCall<AudioVideoCall>(incomingAVCall_CallReceived);

                _logger.Log("Waiting Incoming Calls...");

            }
            catch (RealTimeException ex)
            {
                _logger.Log("Application endpoint establishment failed: {0}", ex);
            }
        }

Next week we'll begin to look at what we do with the incoming calls.


Doug Routledge, C# Lync, Skype for Business, SQL, Exchange, UC Developer  BridgeOC
Twitter - @droutledge @ndbridge


No comments:

Post a Comment

Any spam comments will be deleted and your user account will be disabled.