Writing UAS Applications
A UAS application is an application that is loaded into and run by a UAS.
It is simply a C#, VB, F# or C++ class library assembly that uses the
UASAppAPI and adheres to a few rules.
Each time a UAS application is run it instantiates a class that handles a single primary call
and optionally a small number of secondary extra call channels. It is presented with the call
channel objects that control these calls.
While a call is answered various speech processing features can be
performed on it. Once the call is hung up the application should return. The overall result of
the application run is returned to the UAS and logged.
See
Class Diagram.
Creating a UAS Application Project
Creating a .NET UAS application is a straightforward task using the Visual Studio
project templates supplied with the .NET UAS. These include a template each for inbound and
outbound applications for the .NET languages C#, VB and F#. Currently templates for
Visual Studio 2005, 2008, 2010, 2013 and 2015 are supplied.
To create a new .NET UAS Application project from the Visual Studio
New Project dialog:
Select the language you wish to use -
Visual C#, Visual Basic or Visual F#
(F# is only available from Visual Studio 2010 onwards)
Select
Aculab and one of:
- Aculab UAS Inbound Application
- Aculab UAS Outbound Application
The name entered in the
Name field will determine name of the application, as displayed
in the Management Console and entered in the Inbound or Outbound Service Registration pages via
cloud.aculab.com
your Rapide server.
Ensure that the Target Framework is set to the .Net Framework for your UAS.
The Inbound templates provide code that answers the primary call and then waits for the call to be
hung up.
The Outbound templates provide code that use the outboundParameters argument as a destination
address to initiate an outgoing call.
C++ Projects
As yet, there are no templates available for creation of C++ UAS applications. A sample inbound and
sample outbound application are provided (UASConnectionTest and OutboundReminder) from which other
applications can be derived. C++ UAS application projects can also be created from scratch using
the Visual Studio Class Library project template. Note that The Common Language Runtime Support should be
set to clr:safe.
Inbound/Outbound Applications
A UAS application is either inbound or outbound. This refers to the direction of the primary call
to be managed by the application.
An inbound application inherits from
UASInboundApplication
and is passed a call channel on which an incoming call has been detected and is
waiting to be answered (or rejected).
An outbound application inherits from
UASOutboundApplication
and is passed a call channel on which no call is yet active. The application
is expected to make an outbound call attempt within a reasonably short time.
The UASAppAPI
The UASAppAPI is based on the AMS Client API which provides powerful media processing and call control
features via a high-level client API. It allows incoming and outgoing telephone calls to be controlled and
connected together, wav file playback, wav file record, DTMF playback and detection, and fax transmission
and reception.
The UASAppAPI is supplied as two .NET assemblies that are fully Common Language
Specification (CLS) compliant. All types are declared within the
AMSClassLibrary or
UASAppAPI namespaces.
These assemblies contain classes that control individual telephone calls and associated media processes that
interact with the call. For example, to play a wav file to the call or record the call to file.
This is a synchronous API. Methods that interact with
Aculab Clouda Rapide server
send a message and wait for a response before returning control to the application.
Application Rules
A UAS application is written as a .NET class library assembly that adheres to certain programming rules.
For a UAS application:
- The name of the assembly is the application name.
- The assembly must include a class with the same name as the assembly that inherits from
either UASInboundApplication
or UASOutboundApplication.
- This class must override the abstract
Run
method.
- The
Run
method must return an integer that indicates the overall
success of the application invocation.
- This class may provide a default constructor.
- This class may provide implementations for
OnLoad
and OnUnload
methods.
The entry point for application is the
Run
method. Its prototype is:
Inbound:
public override int Run(UASCallChannel channel, string applicationParameters)
Outbound:
public override int Run(UASCallChannel channel,
string applicationParameters, string outboundParameters)
Where:
channel
- the primary call channel.
applicationParameters
- a string containing parameters associated with the application
(defined in Inbound Services or Outbound Services registration pages.
outboundParameters
- a string containing parameters related to a specific invocation
of the application. Typically this includes the outgoing call destination address.
Return Code:
The return code should adhere to the following restrictions:
- 0 or any positive integer: the application passed. The exact meaning is application specific
- -1 to -99: the application failed, reserved for UAS use
- -1: the application instance has thrown an exception
- -2: the incoming call was hung up before the application instance was invoked
- -3: the specified application is not currently loaded
- -4: the specified application cannot be run
- -5: the application instance returned a reserved value
- -6: the specified application is Inbound, but the call is outbound (or vice versa)
- -7: the UAS is not currently accepting new application requests
- -9: UAS error while processing application request
- any other negative integer: the application failed. The exact meaning is application specific
UASApplication Base Class
All UAS applications inherit from
UASApplication.
This class provides a property
ApplicationInstanceId
that uniquely identifies
a particular application instance within
Aculab Cloud
the Rapide server (this id is included in diagnostic trace for
each instance).
It contains the
FileManager
and
Trace
properties that
provide media file and diagnostic tracing.
It also has the
FaxDocumentToSend
and
FaxDocumentToReceive
properties
that are used when sending or receiving faxes. These last two properties will be
null
unless Fax Sending or Fax Receiving has been enabled within the service invoking a particular
application instance.
Application Initialisation
The .Net UAS supports one-time initialisation of an application so that any static resources that an
application may use can be initialised. To achieve this a UAS application may optionally implement one
or both of the following methods in its application class:
public static int OnLoad();
public static void OnUnload();
The
OnLoad
method is called while an application is being loaded. It should return zero to
indicate success or non-zero to cancel the loading operation.
The
OnUnload
method is called by the UAS when an application is unloaded.
Default Constructor
When a UAS is requested to run a loaded UAS application it instantiates the application's class and then calls
the
Run
method. A default constuctor can optionally be implemented. This will be called immediately
before
Run
is called.
Application Configuration
You can provide application configuration values to be used by your application.
These can either be
- inserted into the UASService.exe's config file
or
- supplied by an application-specific config file
Elements added to the existing UASService.exe.config file should be prefixed with your application name.
They can be accessed from the application using:
String configValue = ConfigurationManager.AppSettings.Get("MyAppName.MyConfigValueKey");
An application-specific config file (e.g. MyAppName.dll.config) residing in the same location as UASService.exe
can be accessed as follows:
ExeConfigurationFileMap configMap = new ExeConfigurationFileMap();
configMap.ExeConfigFilename =
System.IO.Path.Combine(Environment.CurrentDirectory, "MyAppName.dll.config");
Configuration config =
ConfigurationManager.OpenMappedExeConfiguration(configMap,
ConfigurationUserLevel.None);
String configValue =
(String)config.AppSettings.Settings["MyConfigValueKey"].Value;
Diagnostic Trace
The
Trace property of
the
UASApplication class from which
applications inherit provides the ability to log diagnostic trace to file. The amount of trace
is determined by the
TraceLevel
configuration setting for the UAS (the default level is
Error
). This can be altered via the UAS Management Console, from where log files can
also be downloaded for viewing.
The log files are written to a location specified by the
TraceLocation
configuration
UAS setting. This defaults to
Logs
under the installation directory.
The
TraceFileMaxKilobytes
and
TraceFileCount
configuration settings also
affect how large the log files are and how many are written.
(see
Using The UAS for information on configuration)
By default, up to 10 log files are written of 1MB maximum size. e.g.
UAS_000.log - UAS_009.log
In addition to the trace files, Application Data Records (ADRs) are written to the same location
as the diagnostic trace files. These have the form
UAS_ADR_0000.log
.
Debugging
It is possible to debug your UAS application from within Visual Studio by configuring your application to start
the UAS service executable in debug mode.
The steps required are:
-
Ensure the Aculab Cloud
Rapide server .Net UAS service is stopped.
Use the Windows Services Control Manager or run <yourUASInstallDir>\UASService.exe -stop to stop it.
-
Copy your UAS application and it's associated .pdb file to the <yourUASInstallDir>\Applications folder.
-
In your UAS application project within Visual Studio, edit its properties (Debug tab) as follows:
- Set Start external program to <yourUASInstallDir>\UASService.exe
- Set the Command line arguments to -d (to run it in debug mode as a console application)
- Set the working directory to <yourUASInstallDir>
-
Set your UAS application project as Startup Project within Visual Studio.
Start a debug session for your UAS application.
You should now be able to set breakpoints within your UAS application that
will be hit when an instance of your application is started.
Note: When running the UAS service in debug mode for the first time you may need to re-add your cloud
connection via the Management Console.
An application is always supplied with a single primary channel. It is possible to use additional
secondary (extra) channels to make outgoing calls which can, for instance, then be connected
to the primary call.
When registering an application via
Inbound Services or
Outbound Services, the number of extra channels
required can be defined. An application can access the resultant call channels via the ExtraChannels
property of the
UASCallChannel class.
For example
UASCallChannel otherChannel = channel.ExtraChannels[0];
if (otherChannel.Call(otherDestinationAddress) == CallState.Answered)
{
// Connect the calls together
channel.Connect(otherChannel);
}
Text To Speech (TTS)
Aculab Cloud
Rapide
supports Text To Speech (TTS), allowing quick and easy application prototyping
and, more generally, the ability for your applications to read text to their users.
SSML
The TTS methods support the embedding of Speech Synthesis Markup Language (SSML). This is a very
flexible way of expressing how you would like your text to be spoken. Further details on the SSML features
and voices supported are available in the
Aculab Cloud
Rapide
TTS documentation.
In brief, SSML allows you to do things like:
channel.FilePlayer.Say(
"<prosody rate='x-slow'>
This is me speaking slowly.
</prosody>");
channel.FilePlayer.Say(
"<prosody pitch='+50%'>
This is me speaking with a very high pitch.
</prosody>");
Media files to be played to a call or recorded from a call reside on
Aculab Cloud's media file store
the Rapide server.
An application can perform some simple manipulation of these files via the
FileManager
property of
UASApplication from which
the application inherits.
This class enables the application to rename and delete files and check for their existence.
For example:
if (this.FileManager.Exists(recordFileName))
{
this.FileManager.DeleteFile(recordFileName);
}
And encrypted files can be validated which ensures they can be decrypted and are of a supported format before playing:
// Construct the cipher to decrypt the file
AesCbcCipher cipher = new AesCbcCipher(key, initialisationVector);
// If file is validated answer the call and play the file
ValidationResultType result = this.FileManager.FileValidate("wavs/encrypted/encryptedPlay01.wav", cipher);
if (result == ValidationResultType.VALID)
{
channel.Answer();
...
channel.FilePlayer.Play("wavs/encrypted/encryptedPlay01.wav", true, cipher);
}
Additionally,
Aculab Cloud
Rapide provides
Web Services access to manage files
that can be used to download, list upload, rename or delete files. See the
ManageFiles Web Service Client Sample.
Aculab Cloud accesses files via a highly reliable, distributed
storage system.
Your Rapide server accesses files via a local storage
system with some similarities to that on Aculab Cloud.
The way it works will generally be transparent to you as an application writer, but there
are a few important things to bear in mind:
-
When recording a new file within a telephone call, it will be available for use immediately
in that, and within 60 seconds in other calls, from cloud.aculab.com
and from its Web Services API.
When recording a new file within a telephone call, it will be available for use immediately
in that and other calls and within 20 seconds from your Rapide web site
and from its Web Services API.
-
When writing a new file via cloud.aculab.com
or the Web Services API, it will be available for
access within 60 seconds from the completion of the write.
When writing a new file via your Rapide web site
or its Web Services API, it will be available for
access within 20 seconds from the completion of the write.
-
When deleting or overwriting an existing file via cloud.aculab.com
or the Web Services API, the change will take up to 15 minutes to
become available across the distributed storage system. Until this has been accomplished
some telephone calls may continue to access the old version.
When deleting or overwriting an existing file via your Rapide web site
or its Web Services API, the change will take up to 15 minutes to
become available to your applications. Until this has been accomplished
telephone calls will continue to access the old version.
-
While these levels of delay may be fine when you've just published a new version of your
'Hello caller, what would you like to do?' wav file onto
the cloud, it may not be so helpful when you've just recorded a
voicemail and the intended recipient dials into the system soon after to receive it.
your Rapide server, it may not be so helpful when you've just
uploaded an updated emergency announcement and the intended recipient dials into the system
soon after to receive it.
,
So, for cases where you need to access new media files quickly, please make
them new files by using new filenames.
-
It's generally a good idea to organise your files into directories.
The directory delimiter is "/", as in "intro/hello.wav".
Directories are created and destroyed on demand by the storage system so, assuming that the 'intro'
directory doesn't exist, channel.FileRecorder.record("intro/hello.wav") would create that directory
and file_man.delete_file("intro/hello.wav") would remove it along with the file.
Call Transfer
The ability to transfer a call is provided in the form of retrievable transfer.
This re-routes the audio to another destination but maintains control over the
call, allowing it to be retrieved.
This kind of transfer requires an Extra Channel in the Idle state to be supplied.
The Transfer method makes a call to a supplied target address and transfers the
audio connection from the call making the transfer to that target. However it retains
control of the lifetime of the call (this is known as Third Party Call Control in the
SIP world).
This means that the audio connection can be retrieved at a later time. Additionally,
the audio connection returns to the original call automatically, if the target
of the transfer hangs up.
Retrievable transfer is often used in scenarios where an operator creates a call that
connects two participants together and in conferencing.
Conferencing
Aculab cloud
Rapide
supports conferencing together two or more calls (parties)
(see
Conferencing Capabilities)
within an entity called a
conference room for which you provide an identifying name.
Within each conference room a
conference entity controls the audio mix that each party hears.
A number of options can be set for each party as they enter the conference room
(these are shown here in green).
Conference Room
A conference room is
Created when the first party enters
, either by transferring an existing call or by placing a new call to that room.
See:
UASCallChannel.
For example:
CallState state = extraChannel.CallConferenceRoom("BobsConferenceRoom");
if (state == CallState.Answered)
{
channel.Connect(extraChannel);
...
A conference room is
Destroyed if a party exits that is configured to
Destroy On Exit or if the last remaining party exits.
When a conference room is destroyed any remaining parties are automatically hung up.
A party exits a conference room
UASCallChannel.
For example:
channel.HangUp();
- Although the name chosen for a conference room is unique to your user account, you are strongly advised
to provide a different name for each one.
(This avoids various potential issues, for example, where a caller stays in one conference
longer than expected and is suddenly introduced into a supposedly new conference as new callers ring in)
- When transferringconnecting
to a room, a Ringback Tone can be
configured to play while the call is ringing
- When a call is hung up it exits the conference room
- A party can be configured to exit the room when they hit a specified keypad digit
(Exit On DTMF Digit)
- A party may be a human or an application, such as one that records a conference room
Conference
Each conference room contains a single conference that has two states: Started and Stopped.
It is initially in the Stopped state.
While Started all parties in the conference room can either listen or talk and listen.
While Stopped the parties cannot hear each other. Each party may have an
On Conference Stopped File configured to play in this state. An example
would be holding music.
A conference Starts:
When a conference room contains at least two parties, at least one talker and at least one
party configured to Start On Entry.
A conference Stops:
When the number of parties goes below two
or if there are no talkers left in the room. Any parties remaining in the Stopped conference will hear their
On Conference Stopped File if configured
Conference Party Options
When entering a conference room, a number of options determine how the party affects
the conference and how the conference interacts with the party:
Party Type |
The type of participation this party can take in the conference - TalkerAndListener or Listener |
Lifetime Control |
The control that this party has on the lifetime of the conference and conference room. One of:
None |
The party does not directly affect the lifetime of the conference |
Start On Entry |
The conference may start once this party has entered the conference room |
Destroy On Exit |
The conference stops and the conference room is destroyed when this party exits |
Full Control |
The conference may start once this party has entered the conference room and
the conference room will be destroyed when this party exits |
|
Ringback Tone |
The ring tone, if any, that's heard by this party while the call to the room is ringing |
On Conference Stopped File |
An optional name of a media file that is played to the party (looping) while the conference is stopped |
Beep On Entry |
Whether a beep is played to the room when this party enters a started conference |
Exit On DTMF Digit |
A DTMF digit that, if provided, allows this party to exit the conference room by pressing the specified digit (e.g. '*') |
Mute On DTMF Digits |
An optional pair of DTMF digits that allow the party's audio contribution to the conference to be muted and unmuted
when one of the specified digits is hit |
On Entry Media |
An optional name of a media file or some TTS that is played to a started conference immediately before the party enters it
(and after the Beep On Entry, if configured) |
On Exit Media |
An optional name of a media file or some TTS that is played to a started conference immediately after the party exits it |
Prefix Media |
An optional name of a media file or some TTS that is played to a started conference immediately before the On Entry Media or On Exit Media |
For example:
// Create media settings to specify all options
ConferencePartyMediaSettings settings =
new ConferencePartyMediaSettings(
channel.PredefinedToneSequences[PredefinedTones.RingbackUnitedKingdom],
"onHoldMusic.wav", // file to play when stopped
true, // beep enabled
'#', // exit on a #
new ConferencePartyMuteDigits('1', '2'), // Mute on 1, unmute on 2
PlayableMedia.TextToSay("Bob "), // Prefix
PlayableMedia.TextToSay("has entered the conference"),
PlayableMedia.TextToSay("has exited the conference"));
See: ConferencePartyType,
ConferenceLifetimeControl,
ConferencePartyMediaSettings,
ConferencePartyMuteDigits.
Fax
Sending and receiving faxes on a call is supported on
Aculab cloud
Rapide
and needs to be enabled via the Enable fax send and/or Enable fax receive
options in the Fax tab of the invoking Inbound or Outbound service.
Typically faxes are only sent on outgoing calls and received on incoming calls.
Note that enabling either of these will result in the application being charged
at a different platform rate.
Sending a Fax
Enabling fax send in a service creates an empty
FaxDocumentToSend
fax document object as a property of the invoked
UASApplication.
The content of this object can be built up from TIFF files that have been stored
on the
cloud
Rapide server
and text can be added to selected pages.
It can be sent using the FaxSender property of
a call channel.
For example:
this.FaxDocumentToSend.SetContent("myFaxTxDocument.tif");
channel.FaxSender.Send(this.FaxDocumentToSend);
Also see the OutboundSendFax
application sample.
Receiving a Fax
Enabling fax receive in a service creates a
FaxDocumentToReceive
fax document object as a property of the invoked
UASApplication.
The filename of this object must be set, then a fax can be received using the
FaxReceiver property of a call
channel.
For example:
this.FaxDocumentToReceive.SetFileName("receivedFax1.tif");
channel.FaxReceiver.Receive(this.FaxDocumentToReceive);
Also see the InboundReceiveFax
application sample.
Advanced Fax Options
Typically faxes can be sent and received without recourse to changing the Fax Options.
However, where circumstances require it, the default fax options for a service can be
altered from the Fax tab of
Inbound Services and
Outbound Services.
These defaults may be overriden per application instance via the
FaxOptions
class that can be passed to the FaxSender or FaxReceiver. Option names and value ranges
must match those available on the Service Fax tab.
See Sending & Receiving Faxes
for more information.
High Level API
Additional high level functionality on top of the standard UASCallChannel
is provided by the CallChannelHelper class.
The CallChannelHelper
class has methods to:
- Make an outbound call and connecting it to an existing call (CallAndConnect)
- Play a prompt, obtain a speech response or (optionally) digit keypresses (CaptureInput)
- Play a prompt, obtain a specified number of digit keypresses e.g. pin code, card number etc. (CaptureNumber)
- Play a prompt, obtain a variable number of digit keypresses up to an end digit e.g. '#' (CaptureNumber)
- Play a prompt, obtain any specified DTMF keypresses (CaptureValidatedDigits)
- Present a menu of options to be selected by digit keypress (InvokeMenu)
For example:
// Make an outbound call and connecting it to an existing call
CallChannelHelper channelHelper = new CallChannelHelper(this, channel);
// Establish the target address
CallTarget callTarget = CallTarget.SipAddressToCall(
"sip:bob@acompany.com", "sip:bill@anothercompany.com");
channelHelper.CallAndConnect(callTarget, channel.ExtraChannels[0], 120);
// Wait for 10 mins for either call to be hung up
WaitForAnyCallIdle(
new UASCallChannel[] { channel, channel.ExtraChannels[0] }, 600);
// Play a prompt, obtain a speech response
CallChannelHelper channelHelper = new CallChannelHelper(this, channel);
Grammar ukCountries = Grammar.CreateFromAlternatives(
new String[] { "England", "Wales", "Scotland", "Northen Ireland" });
PlayableMedia prompt =
PlayableMedia.TextToSay(
"Please say the name of a constituent country of the United Kingdom.");
// Capture speech
CapturedInput capturedInput = channelHelper.CaptureInput(prompt, ukCountries);
if (capturedInput != null)
{
channel.FilePlayer.Say("You said: {0}", capturedInput.Input);
}
}
// Play a prompt, obtain a specified number of digit keypresses
CallChannelHelper channelHelper = new CallChannelHelper(this, channel);
PlayableMedia prompt =
PlayableMedia.TextToSay("Enter a 12 digit number");
string entry = channelHelper.CaptureNumber(prompt, 12);
// Present a menu of options to be selected by digit keypress
CallChannelHelper channelHelper = new CallChannelHelper(this, channel);
PlayableMedia prompt =
PlayableMedia.TextToSay("Press 1 to do something,
press 2 to do something else.");
char selection = channelHelper.InvokeMenu(prompt, "12");
if (selection == '\0')
{
channel.FilePlayer.Say("You didn't select anything...");
}
else
{
channel.FilePlayer.Say("You selected: {0}", selection);
...
}
SIP-specific Features
SIP-specific features such as access to SIP headers, SIP INFO and SIP MESSAGE
requests are implemented in the
SIP,
SIPHeaderField,
SIPInfo and
SIPMessage classes.
Referencing External Libraries
3rd-party or other external libraries may be referenced by your applications.
Currently this requires some manual intervention to copy any referenced
library files to the [UASInstallFolder]. The applications themselves
can be installed in the normal way via the Management Console.
Please note that the UAS service will need to be stopped before
any libraries that have been copied in this way can be upgraded.
External library files copied to the [UASInstallFolder] will not
be removed by uninstalling the UAS package and will be retained
when a new UAS package is installed to the same location.