application initialization using avaya ae services tsapi ... · application initialization using...
TRANSCRIPT
Application Initialization using Avaya AE Services TSAPI SDK for Windows A Devconnect Tutorial
Table of Contents
Section 1: Avaya AE Services and
TSAPI Overview .......................3
Section 2: Session Management ..............7
Section 3: Control Services and the
Request/Response Model .... 18
Section 4: Event Handling ...................... 20
Section 5: Private Data ........................... 33
Section 6: Setting Up the TSAPI
Programming Environment
................................................ 43
Section 7: A Practical Example ............. 46
Appendix A: Notes ...................................... 51
Appendix B: Complete Example Code .. 54
Appendix C: References ............................77
Overview
Implementing a telephony-enabled application using Avaya Telephony Services
Application Programming Interface (TSAPI) requires an understanding of TSAPI core
concepts of Session and Event Management, control services, and private data.
This tutorial provides an overview of the TSAPI essentials, using code samples
to demonstrate how to write a telephony-enabled application for the Avaya
Communication Manager environment using the interfaces provided by the
Avaya TSAPI implementation.
INTENDED AUDIENCE
This tutorial is intended for programmers who have a working knowledge of C/
C++ programming on Windows platform and wish to implement telephony-
enabled applications in an Avaya Aura™ Communication Manager environment.
This tutorial is not meant to be an exhaustive introduction to TSAPI; refer to
Reference [1] for a complete understanding of the Avaya TSAPI implementation.
After completing this tutorial, the reader should be able to:
• Understand the TSAPI concepts.
• Initialize a telephony-enabled application using the Avaya TSAPI library.
• Open a Session with TSAPI Service with (or without) Private Data negotiation.
• Receive events from TSAPI Service.
• Start and stop monitoring a device.
• Handle error conditions.
• Close the Session with TSAPI Service.
• Abort the Session with TSAPI Service.
DOCUMENT ORGANIZATION
This tutorial begins with an overview and description of all the required
TSAPI concepts, and makes the reader familiar with the TSAPI library. It then
1DEVCONNECT TUTORIAL
avaya.com
ISSUE 1.0 • SEPTEMBER 2009
avaya.com
2
discusses how to set up the environment for the TSAPI application, and closes with a complete practical source
code example which makes use of all the TSAPI concepts covered in prior chapters.
• Chapter 1: Avaya AE Services and TSAPI Overview
This chapter provides the reader with a brief understanding of Avaya Application Enablement (AE) Services,
the purpose of the Avaya TSAPI library, and its basic capabilities. In this chapter, the reader will find brief
definitions of the concepts that are essential to develop a TSAPI application.
• Chapter 2: Session Management
This chapter explains:
a) The meaning of Session in TSAPI terminology.
b) Why a Session is required.
c) How a Session can be opened.
d) How a Session can be closed.
• Chapter 3: Control Services and the Request/Response Model
This chapter describes the types of Control Services, their usage and the request/response model that TSAPI supports.
• Chapter 4: Event Handling
This chapter explains the types of events, different ways to receive events, and the process to extract
information from events.
• Chapter 5: Private Data
This chapter provides basic information on handling Private Data including negotiating, and sending and
receiving Private Data.
• Chapter 6: Setting up the TSAPI Programming Environment
This chapter guides the reader in setting up and configuring the programming environment required to write and
run a TSAPI application.
• Chapter 7: A Practical Example
The final chapter includes a step by step guide demonstrating a C++ implementation of a telephony-enabled
application for a Microsoft Windows platform using the Avaya TSAPI SDK. The examples provided in this tutorial
are kept as simple as possible so as to help the reader understand key concepts which can then be applied for
building advanced applications.
Appendices, including notes, a complete source code listing, and references are also included.
avaya.com
3
PREREQUISITES
Readers are assumed to have C++ programming experience and knowledge of Avaya Aura Communication Manager
and Avaya Application Enablement (AE) Services.
The Avaya TSAPI SDK is required to develop a TSAPI application. The Avaya TSAPI Client library is required to
run a TSAPI application. Both the TSAPI Client and TSAPI SDK need to be installed and configured properly on
the machine(s) on which the TSAPI application is to be developed and/or executed. Please refer to Reference
[2] for information on how to install and configure the Avaya TSAPI Client and the TSAPI SDK. Information on
downloading or obtaining the AE Services TSAPI Client and TSAPI SDK is available on the DevConnect Portal.
See www.avaya.com/devconnect (valid DevConnect ID is required to access certain materials).
ACRONYMS USED IN THIS TUTORIAL
Acronym MeaningAE Services Avaya Application Enablement Services
TSAPI Telephony Services Application Programming Interface
CTI Computer Telephony Integration
SDK Software Development Kit
ECMA European Computer Manufacturers Association
API Application Programming Interface
CSTA Computer-Supported Telecommunications Applications
Table 1: List of acronyms used in this tutorial
Section 1: Avaya AE Services and TSAPI Overview
This chapter describes the underlying platform and services that enable developers to write telephony-enabled
applications and the usage of the Avaya software suite. This chapter also helps the reader to understand the role of
each of the components described below in supporting Computer Telephony Integration (CTI).
1.1 AVAYA AURA™ COMMUNICATION MANAGER
Avaya Aura Communication Manager is Avaya’s flagship IP Telephony software platform. It contains robust call
processing capabilities, advanced workforce productivity and mobility features, built-in conferencing and contact
center applications, and support for a variety of wired and wireless end-user communication devices. Avaya Aura
Communication Manager is the call processing software that runs on Avaya’s servers. Communication Manager
processes call requests and allows the user to retrieve and use call information.
Communication Manager offers a variety of first and third-party call control capabilities and event notifications
APIs, although these are not directly exposed for application development. It is through Avaya Application
Enablement (AE) Services that the capabilities of the Communication Manager APIs are exposed as high level
standards based application programming interfaces (APIs).
avaya.com
4
1.2 APPLICATION ENABLEMENT (AE) SERVICES
AE Services is a server-based software application that provides connectivity between client applications and Avaya
Communication Manager. AE Services incorporates a number of discrete services which provide high-level abstractions
of the Communication Manager API. These AE Services APIs allow other software products to interface with
Communication Manager using services provided by Avaya AE Services. The APIs allow software developers to create
their own client applications that interact with, and leverage the capabilities of, Avaya Communication Manager.
AE Services enables software developers to write client applications using a wide variety of programming languages
such as C, C++, Java, etc.
Figure 1 below depicts a high level view of how AE Services, Avaya Communication Manager, Avaya endpoints and
client communication applications align:
Figure 1: AE Services server architecture
AE Services offers several different types of services, each exposed through different combinations of APIs and
SDKs. These include:
• System Management capabilities, exposed as web services;
• Basic Telephony services, also exposed as Web Services;
avaya.com
5
• Device, Media and Call Control services, exposed through Java, XML or .Net interfaces;
• And TSAPI services, exposed via a TSAPI client library using a C/C++ API, or via a Java Telephony API (JTAPI)
client library for Java-based application development.
Each of these AE Services APIs offer slightly different levels of functionality, and the choice of which one is most
appropriate to use is dependent upon the needs of the application under development (Table 2):
Programming Language, Protocol or Web ServicesFunctionality Java .NET C and C++ Other / XML Web Services
Advanced third- party call control
JTAPI TSAPI
Basic third-party call control
DMCC Java API
DMCC .NET API
DMCC XML protocol
DMCC XML protocol
Simple call creation and maintenance
Telephony
Physical device control
DMCC Java API
DMCC .NET API
DMCC XML protocol
DMCC XML protocol
Media control DMCC Java API
DMCC .NET API
DMCC XML protocol
DMCC XML protocol
Communication Manager system management
System Management Service
Table 2: Comparison of AE Services SDKs
Not explicitly shown in Figure 1 is an LDAP store, containing the Security Database and CTI User credentials. This
information is accessed by the TSAPI Service and used for the purpose of managing application’s access of AE
Services device monitoring and control.
1.3 ABOUT TSAPI
The Telephony Services API (TSAPI) is based on international standards for CTI telephony services. Specifically,
the European Computer Manufacturers Association (ECMA) CTI standard definition of Computer-Supported
Telecommunications Applications (CSTA) is the foundation for TSAPI. The CSTA standard is a technical agreement
reached by an open, multi-vendor consortium of major switch and computer vendors. Since CSTA services and
protocol definitions are the basis for TSAPI, TSAPI provides a generic, switch-independent API.
1.3.1 TSAPI for Communication Manager
TSAPI for Avaya Communication Manager is an implementation of the generic TSAPI specification. Stated another
way, TSAPI for Avaya Communication Manager is a switch-specific API that helps C and C++ programmers to
implement CTI in the Avaya Communication Manager environment.
avaya.com
6
TSAPI for Communication Manager is a library interface that is designed exclusively for use with Avaya
Communication Manager.
1.3.2 TSAPI Service
TSAPI Service is one of the services that run on AE Services server. TSAPI Service acts as a server for the TSAPI
Client running on the client machine. TSAPI Service receives requests from the TSAPI client, processes these
requests with the help of Communication Manager and returns the response back to the TSAPI Client.
1.3.3 TSAPI Client
The TSAPI Client provides applications with access to Avaya Communication Manager’s call processing services.
The primary component of the TSAPI Client is the TSAPI library, which is a C/C++ library of function calls
that enables an application to request Computer-Supported Telecommunications Applications (CSTA) services.
Additionally, the TSAPI Client provides access to Avaya Private Data, which extends the CSTA supported
capabilities by providing access to specialized features of Avaya Communication Manager.
1.4 ARCHITECTURE OF A TYPICAL TSAPI APPLICATION
A TSAPI application uses the TSAPI Client library which is a C/C++ based client-side interface of TSAPI Service.
The TSAPI Client library establishes a connection with TSAPI Service, sends service requests and receives
responses, on behalf of the client application.
TSAPI provides third party call control capabilities provided by Avaya Communication Manager. These third party
call control capabilities include:
• The ability to make, monitor, answer, transfer, hold, retrieve, deflect, conference, and drop calls.
• The ability to monitor station and Vector Directory Number (VDN) devices for call activity.
• Control and interaction of calls in vector processing, predictive dialing and call classification, or skill-based routing.
• Providing a snapshot of a device which returns information regarding the calls on that device, and a snapshot of
a call which returns information regarding the parties involved in the call.
• Performing logical services such as Agent login/logout, Agent state and device feature control.
• Monitoring ACD (Automatic Call Distribution) Split devices for Agent login/logout events.
avaya.com
7
Figure 2: Architecture of TSAPI Application
Figure 2 depicts how the TSAPI Client library connects the TSAPI applications to TSAPI Service. Note that it is
possible to leverage one instance of the TSAPI Client Library on a server in support of multiple applications, as
shown on Server 1 (machine 1) where two TSAPI applications are running. Both the applications use the same
version of the TSAPI Client library. On Server 2 (machine 2), a single TSAPI application is running, and all three
applications (across both machines) are invoking a common TSAPI Service executing on Avaya AE Services. API
Control Services (ACS) streams are used to manage the interactions with TSAPI Service running on the AE Services
server for passing maintaining sessions and handling event queues.
Section 2: Session Management
This chapter describes ‘Session’ as used with Avaya TSAPI applications, why a Session is required, how does a
TSAPI application open a Session with TSAPI Service to receive data and events, and how to close a Session.
Like any client-server based application, the TSAPI application (i.e., the client) needs to first establish a
communication channel with TSAPI Service before requesting any services from it. This communication channel
is then used to send and receive information by the TSAPI application. This communication channel is called
‘Session’ in general terms. A Session is a logical entity and represents a dedicated link between two parties, one
who establishes the Session (client) and the other (server) who accepts it. Once a Session is established, both the
parties can send and receive data on this communication link. A Session exists until it is either closed explicitly
(by a close request) or closed due to a problem (e.g. loss of network connectivity).
Avaya TSAPI refers to this communication channel as a “stream”. To access any services from TSAPI Service,
an application must first open a stream (or Session). The opened stream establishes a logical link between the
application and Communication Manager (via AE Services).
avaya.com
8
A TSAPI application requires an advertised service name for opening a stream. The TSAPI application can read
the advertised service name from a file (e.g. .ini file), or query the AE Services server using acsEnumServerNames()
method to get the list of advertised service names. Refer to Section 2.1.1 to know more about the
acsEnumServerNames() method.
2.1 ACQUIRING THE NAME OF AN ADVERTISED SERVICE
An advertised service (sometimes referred as advertised server) represents a symbolic name for a communication
path between AE Services server and Communication Manager. A TSAPI application will need to choose the
communication path through which AE Services server will route requests to Communication Manager. TSAPI
provides the acsEnumServerNames() method that can be used by a TSAPI application to get all the advertised
services of the given type, offered by AE Services server.
When this method is invoked by the application, the TSAPI Client library reads the Fully Qualified Domain Name
(FQDN) or IP addresses of AE Services server(s) from the TSAPI Client configuration file (i.e., TSLIB.INI). Using
these FQDN or IP addresses, the TSAPI Client library queries each AE Services server for advertised services of a
specific stream type.
Using the acsEnumServerNames() method, the user can specify a callback method that will be called for each
service name. For each advertised service name returned by each AE Services server, the TSAPI Client library
invokes the callback method with the serverName parameter set to the advertised service name. Both callback and
acsEnumServerNames() methods are described below.
2.1.1 EnumServerNames Callback Method
The application need to implement a callback method which has a specific signature (Code Snippet 1).
EnumServerNamesCB Method’s Signature:
typedef Boolean (*EnumServerNamesCB)( char *serverName, /* INPUT */
unsigned long lParam); /* INPUT */
Code Snippet 1: Enum Server NamesCB Method’s Signature
EnumServerNamesCB Method’s Parameters:
• serverName: A null-terminated string representing an advertised service name.
• lParam - A user-defined parameter which is passed through acsEnumServerNames() method.
Note: The application can choose any name for the callback method as long as the signature matches the
signature described above.
avaya.com
9
EnumServerNamesCB Method’s Return Values:
The callback method needs to return a boolean value. When the callback method returns false, the TSAPI client
will not call the callback method with other service names, whereas true return value indicates that the TSAPI
client can continue to call the callback method with the next service name.
2.1.2 acsEnumServerNames Method
acsEnumServerNames Method’s Signature:
RetCode_t acsEnumServerNames( StreamType_t streamType, /* INPUT */ EnumServerNamesCB callback, /* INPUT */
unsigned long lParam); /* INPUT */
Code Snippet 2: acsEnumServerNames Method’s Signature
acsEnumServerNames Method’s Parameters:
• streamType - Indicates the type of stream requested. Currently, only a stream of type ST_CSTA is supported.
• callback - This is a pointer to a callback method which will be invoked for each of the enumerated server
names, along with the user-defined parameter lParam. If the callback method returns FALSE (0), enumeration
will terminate.
• lParam - A user-defined parameter which is passed on each invocation of the callback method. The application can
use this parameter to pass any value to the callback method when the Client library invokes the callback method.
acsEnumServerNames Method’s Return Values:
A positive return value indicates success and a negative return value indicates failure. There is no confirmation
event for this method. The positive return value is:
• ACSPOSITIVE_ACK: The method completed successfully as requested by the application. No errors were detected.
Possible errors are (negative return value):
• ACSERR_UNKNOWN: The request has failed due to unknown network problems.
• ACSERR_NOSERVER: The request has failed because the Client library is failed to locate one of the AE Services
server’s IP address on the network. This error can arrive if any of the AE Services server’s IP address is not
configured properly.
avaya.com
10
acsEnumServerNames Code Example:
// Callback method that will be called for each advertised service name.// szServiceName method parameter will be set to advertised service name or Tlink// when callback method is called. // lParam will be set to the value of user defined variable provided in// acsEnumServerNames method call.// Return FALSE to stop receiving callback for rest of the service names. Boolean DisplayServerNames(char* szServiceName, unsigned long lParam) { Boolean bReturnValue = FALSE;
if(strcmp(szServiceName, "") != 0) {
// Service name is printed on console here for simplicity, other applcations// can store this value into a map or any other data structure// and use it later as per application requirement. // Refer complete example code to see storing service name in a map.
cout << "Service Name: " << szServiceName;
// Return TRUE so that the callback is called // again with the name of the next advertised // service found.
bReturnValue = TRUE; }
else {
// Method received an empty buffer for service name, // returning false in this scenario will cause stop // receiving any more Service name i.e. this method // will not be called again.
} return bReturnValue;
}
// This method uses acsEnumServerNames() TSAPI method to specify// a callback method that will be called for each service name. void EnumerateServiceNames() {
// Enumerate the names of all servers of a specified stream type. RetCode_t nRetCode = acsEnumServerNames(ST_CSTA, // Enumerate CSTA services DisplayServerNames, // callback method defined above NULL // user defined variable, // value of this variable // will be passed to the callback method as // lParam parameter. Passing NULL here to keep // the example code simple. );
if ( nRetCode != ACSPOSITIVE_ACK ) {
// some error occurred while sending request. cout<<" Error Code: "<<nRetCode;
// handle error. }
}
Code Snippet 3: Enumerating Advertised Services
avaya.com
11
2.2 OPENING A STREAM
The acsOpenStream() method is used for a request to open a stream with TSAPI Service. An application needs an
API Control Services (ACS) stream to access other API Control Services or CSTA Services. Thus, an application
must call acsOpenStream() before requesting any other ACS or CSTA service.
The acsOpenStream() method returns immediately with a return value indicating partial success or failure;
a confirmation event that confirms the success arrives later.
The acsOpenStream()method returns a handle (acsHandle) which references the opened ACS stream. The
application will use this acsHandle to access the ACS stream (to make requests and receive events).
When a stream is successfully opened, the application receives an ACSOpenStreamConfEvent event that corresponds
to the acsOpenStream() request. The application must wait for the ACSOpenStreamConfEvent event before requesting
any other ACS or CSTA service.
If the application needs to use extended features supported by a specific Private Data version then the
application can specify the Private Data version in the acsOpenStream()method call. In such case, when
the ACSOpenStreamConfEvent event is received, the application needs to validate that required Private Data
version is negotiated successfully. The application must use the same Private Data version returned in the
ACSOpenStreamConfEvent event and request only the features that are supported in the returned Private Data
version. To see the list of features supported in different Private Data versions, refer to Reference [1], Appendix B,
“Summary of Private data support”. For more information regarding Private Data version negotiation procedure, see
Section 5.1.
To match a service request with their corresponding confirmation/failure events, the application can use invokeID.
To know more about invokeID, see Appendix A: Notes, Note 1, “What is InvokeID?”
Note the following important points:
1. The application should always wait to receive the ACSOpenStreamConfEvent event before requesting any CSTA
Service requests.
2. The application is responsible for releasing its ACS stream(s). To release the system resources associated with
an opened (or active) ACS stream, the application may either close the stream or abort the stream. When the
application is designed to work with multiple streams, then it becomes even more important to release the
previously opened streams when they are not required (or not is use).
3. An acsHandle is a local process identifier and should not be shared across processes.
avaya.com
12
2.2.1 acsOpenStream Method
acsOpenStream Method’s Signature:
RetCode_t acsOpenStream( ACSHandle_t *acsHandle, /* OUTPUT */ InvokeIDType_t invokeIDType, /* INPUT */ InvokeID_t invokeID, /* INPUT */ StreamType_t streamType, /* INPUT */ ServerID_t *serverID, /* INPUT */ LoginID_t *loginID, /* INPUT */ Passwd_t *passwd, /* INPUT */ AppName_t *applicationName, /* INPUT */ Level_t acsLevelReq /* INPUT */ Version_t *apiVer, /* INPUT */ unsigned short sendQSize, /* INPUT */ unsigned short sendExtraBufs, /* INPUT */ unsigned short recvQSize, /* INPUT */ unsigned short recvExtraBufs /* INPUT */ PrivateData_t *privateData); /* INPUT */
Code Snippet 4: acsOpenStream() Method’s Signature
Applications calling the acsOpenStream()method need to pass a set of parameters for setting up the environment
and performing a handshake between themselves and TSAPI Service. Each parameter of the acsOpenStream()
method is described below:
acsOpenStream Method’s Parameters:
• acsHandle: The acsOpenStream service request returns this value that identifies the ACS stream that was
opened. The TSAPI library sets this value so that it is unique to the ACS stream. Once acsOpenStream() is
successful, the application must use this acsHandle in all other method calls to TSAPI Service on this stream.
If acsOpenStream() is successful, the TSAPI library guarantees that the application has a valid acsHandle.
• invokeIDtype: The application sets the type of invoke identifiers used on the stream being opened. Two
possible values are APP_GEN_ID for Application-generated invokeIDs and LIB_GEN_ID for Library generated
invokeIDs. For more information on InvokeIDType, refer to Note 1 What is InvokeID.
• invokeID: The application supplies this handle for matching the acsOpenStream() service request with its
confirmation event. An application supplies a value for invokeID only when the invokeIDtype parameter is set
to APP_GEN_ID. TSAPI ignores the invokeID parameter when invokeIDtype parameter is set to LIB_GEN_ID.
For more information on InvokeID, refer to Note 1 What is InvokeID.
• streamType: The application provides the type of stream in streamType. The value of streamType when opening
ACS stream for CSTA services needs to be:
— ST_CSTA - identifies a request as a CSTA call control stream. This stream can be used for TSAPI Service
requests and responses which begin with the prefix “csta”.
• serverID: The application provides a null-terminated string of maximum size ACS_MAX_SERVICEID. This string
contains the name of an advertised service (in ASCII format). The application must ensure that the serverID
advertised service provides services of the type given in the streamType parameter. An application can use
avaya.com
13
acsEnumServerNames()method to get the list of advertised services of the given type offered by the AE
Services server. Please refer to Section 2.1.2 for additional details.
• loginID: The application provides a pointer to a null terminated string of maximum size ACS_MAX_LOGINID. This string
contains the login ID of the CTI user requesting access to the advertised service given in the serverID parameter.
• passwd: The application provides a pointer to a null terminated string of maximum size ACS_MAX_PASSWORD.
This string contains the password of the CTI user given in loginID.
• applicationName: The application provides a pointer to a null terminated string of maximum size ACS_MAX_
APPNAME. This string contains an application name. The AE Services system uses the application name on
certain administration and maintenance status displays.
• acsLevelReq: This parameter is ignored.
• apiVer: The application uses this parameter to specify the TSAPI version it can work with. This parameter
contains a string beginning with the characters “TS” followed by an ASCII encoding of one or more version
numbers. An application may use the “-” (hyphen) character to specify a range of versions and the “:” (colon)
character to separate a list of versions. For example, the string “TS1-3:5” specifies that the application is
willing to accept TSAPI versions 1, 2, 3, or 5.
• sendQSize: The application specifies via sendQsize the maximum number of outgoing messages the TSAPI
Client library will queue before returning ACSERR_QUEUE_FULL. If the application supplies a zero (0) value,
then a default queue size will be used.
• sendExtraBufs: The application specifies the number of additional packet buffers that TSAPI needs to allocate
for the send queue. If sendExtraBufs is set to zero (0), the number of buffers is equal to the default queue
size (i.e., one buffer per message). If the user expects messages to exceed the size of a network packet
(which can happen if the application use Private Data extensively), they should allocate additional buffers
i.e., the application needs to increase the sendExtraBufs value. Also, if the application frequently receives the
error ACSERR_NOBUFFERS, it indicates that the application has not allocated enough buffers.
• recvQSize: The application specifies via recvQSize the maximum number of incoming messages the TSAPI
Client library queues before it ceases acknowledgment to AE Services server. TSAPI uses a default queue
size when recvQSize is set recvExtraBufs: The application specifies via recvExtraBufs the number of additional
packet buffers that TSAPI needs to allocate for the receive queue. If recvExtraBufs is set to zero (0), the
number of buffers is equal to the default queue size (i.e., one buffer per message). If the messages exceed
the size of a network packet, as is the case where Private Data is used extensively, or the application
frequently sees ACSERR_STREAM_FAILED, it indicates that the application has not allocated a large enough
buffer using recvExtraBufs i.e., the application needs to increase the size of recvExtraBufs.
• privateData: The application uses this parameter to provide a pointer to a data structure that contains any
implementation-specific initialization. For TSAPI Service, this pointer is used to specify Avaya Private Data.
The TSAPI protocol does not interpret the data in this structure. An application can pass version negotiation
information in the Private Data parameter of the acsOpenStream() method. An application that does not use
avaya.com
14
Private Data should pass NULL in this parameter while calling the acsOpenStream() method. Refer to Chapter 5
for detailed information about Private Data and Private Data version negotiation.
acsOpenStream Method’s Return Values:
acsOpenStream() method’s return value follows the same convention as described in Note 2, Common Return Values.
The application should always check the ACSOpenStreamConfEvent message to ensure that TSAPI Service has
positively acknowledged the acsOpenStream() request.
acsOpenStream() returns the following negative error conditions:
• ACSERR_APIVERDENIED - The requested API version (apiVer) is invalid or the client library does not support it.
• ACSERR_BADPARAMETER - One or more of the parameters are invalid.
• ACSERR_NODRIVER - No TSAPI Client library driver was found or installed on the system.
• ACSERR_NOSERVER - The advertised service (serverID) is not available in the network. The application can also
receive this error when the application is trying to access a secured TLink. For more information, see Note 4,
Accessing a Secured TSAPI Link (Tlink).
• ACSERR_NORESOURCE - There are insufficient resources to open an ACS stream.
• ACSERR_SSL_INIT_FAILED - This return value indicates that a secure connection could not be opened because
there was a problem initializing the OpenSSL library.
• ACSERR_SSL_CONNECT_FAILED - This return value indicates that a stream could not be opened because there
was a problem establishing an SSL connection to the server. It may be that the server failed to provide a
certificate, or that the server certificate is not signed by a trusted Certificate Authority.
• ACSERR_SSL_FQDN_MISMATCH - This return value indicates that a stream could not be opened because the
FQDN (Fully Qualified Domain Name) in the server certificate does not match the expected FQDN.
• acsOpenStream() may also return ACSERR_STREAM_FAILED if the application attempts to open a stream to a secure
(encrypted) Tlink but the TSAPI Client library (release 4.0.x or earlier) does not support secure client connections.
Some failures are detected by the AE Services server and are thus not available immediately from the method’s
return value. The application must be prepared to receive any of the following events anytime after the
acsOpenStream() method completes:
• an ACSUniversalFailureConfEvent (when there is an error related to ACS Stream),
• CSTAUniversalFailureConfEvent (error while processing CSTA request), or
• ACSUniversalFailureEvent (when ACS stream is failed).
avaya.com
15
These events contain the failure cause and also indicate that a failure has occurred on the stream. Some of the
failures that can be received in these events are tserverBadPasswordOrLogin, tserverNoUserRecord, etc. Please
refer to Appendix B: Complete Example Code to see how these events are handled in the Notify() method.
A TSAPI client application can also access a secured Tlink (or advertised service name or Server ID). For more
information, see Appendix A: Notes, Note 4, ”Accessing a Secured TSAPI Link (Tlink).”
acsOpenStream Code Example:
The following code snippet shows a call to the acsOpenStream() method. Refer to the OpenACSStream() method
in Appendix B: Complete Example Code for the complete procedure of calling the acsOpenStream() method.
// Open a stream with Private Data negotiation & with library// generated InvokeID. This method will returns invokeID on success // as LIB_GEN_ID is used. The invokeID will be stored in nRetCode.
// If the application does not wish to receive Private Data, it should // pass NULL in privateData parameter.
RetCode_t nRetCode = acsOpenStream(acsHandle, LIB_GEN_ID, // Library takes the control for generating InvokeID. 0, // This param is ignored when 2nd parameter is LIB_GEN_ID ST_CSTA, // requesting CSTA stream type. (ServerID_t*)"AVAYA#CMSIM#CSTA#AESSIM", // CTI Link name // AVAYA#SWITCH1#CSTA#SERVERNAME1" (LoginID_t*)"avaya", // CTI user login ID
(Passwd_t*)"Avayapassword", // CTI user password (AppName_t*)"DeviceMonitor", // name of the application ACS_LEVEL1, // LevelReq, This parameter will be ignored
(Version_t*) "TS1-2", // API Version 0,// send queue size using default 5, // send extra buf size 0, // receive queue size using default 10, // receive extra bufs (PrivateData_t *)&privateData // includes version negotiation information. // Refer to section 5.1 to see how to prepare // version negotiation information. );
Code Snippet 5: acsOpenStream() Code Example
2.3 CLOSING A STREAM
Similar to opening a stream, a client application also needs to close a stream. Once the client application finishes
its operations and is about to exit, it should close all the open stream(s) to free up the system resources consumed
by the opened stream(s).
The application uses the acsCloseStream()method to close an ACS stream. The application will be unable to
request services from TSAPI Service after the acsCloseStream() method has returned successfully.
avaya.com
16
2.3.1 acsCloseStream Method
acsCloseStream Method’s Signature:
RetCode_t acsCloseStream ( ACSHandle_t acsHandle, /* INPUT */ InvokeID_t invokeID, /* INPUT */ PrivateData_t* privateData /* INPUT */ );
Code Snippet 6: acsCloseStream() Method’s Signature
acsCloseStream Method’s Parameters:
• acsHandle - This is the handle for the active ACS stream which is to be closed. Once the confirmation event
associated with this request is received, the handle is no longer valid.
• invokeID - A handle provided by the application, used for matching a confirmation event with this request. This
parameter is used only when the InvokeID mechanism is set for application-generated IDs in acsOpenStream( ) method.
This parameter is ignored by the TSAPI Client library when the stream is set for library-generated invoke IDs.
• privateData - This parameter is ignored. Hence, a NULL is required to be passed in this parameter for the
acsCloseStream() method.
acsCloseStream Method’s Return Values:
The acsCloseStream() method’s return value follows the same convention as described in Note 2, Common Return Values.
The possible negative error condition for acsCloseStream() method is:
• ACSERR_BADHDL - This indicates that the acsHandle being used is not a valid handle for an active ACS
stream. No changes occur in any existing streams if a bad handle is passed with this method.
When the acsCloseStream() method returns with a non-negative value, the application must wait to receive the
ACSCloseStreamConfEvent event to ensure that the service request has been processed successfully by AE Services
server and the ACS stream was properly closed. No new service requests will be accepted for the specified acsHandle
after this method successfully returns. However, the handle is still active and valid until the application receives the
ACSCloseStreamConfEvent i.e., the application will continue to receive pending events on this stream.
See Chapter 7: for information on handling the ACSCloseStreamConfEvent event.
acsCloseStream Example Code:
A sample code snippet for the acsCloseStream() method is given below. Refer to the CloseStream() method in
Appendix B: Complete Example Code for the complete procedure of calling the acsCloseStream() method.
// Request to close an ACS stream. A confirmation event will be provided for this// method. RetCode_t nRetCode = acsCloseStream(acsHandle, // Handle of the active ACS stream 0, // Using library generated invokeID, passing 0 NULL);, // Private data not used, passing NULL
Code Snippet 7: acsCloseStream() Code Example
avaya.com
17
2.4 ABORTING A STREAM
This method unilaterally closes an ACS stream to TSAPI Service. The application will be unable to request services
from TSAPI Service or receive events after the acsAbortStream() method has returned. The acsHandle is invalid on
this stream after the acsAbortStream() method returns. There is no associated confirmation event for this method.
2.4.1 acsAbortStream Method
acsAbortStream Method’s Signature:
RetCode_t acsAbortStream( ACSHandle_t acsHandle, /* INPUT */ PrivateData_t* privateData /* INPUT */ );
Code Snippet 8: acsAbortStream() Method’s Signature
acsAbortStream Method’s Parameters:
• acsHandle - This is the handle for the active ACS stream which is to be aborted. Once acsAbortStream()
method returns successfully, the handle becomes invalid.
• privateData – This parameter is ignored. Hence, a NULL is required to be passed in this parameter for the
acsAbortStream() method.
acsAbortStream Method’s Return Values:
This method returns zero (0) if successful. The possible negative error condition for this method is:
• ACSERR_BADHDL - This indicates that the acsHandle being used is not a valid handle for an active ACS
stream. No changes occur in any existing streams if a bad handle is passed to this method.
acsAbortStream Example Code:
The following code snippet shows the procedure to invoke the acsAbortStream() method. Refer to the AbortStream()
method in Appendix B: Complete Example Code for the complete procedure of calling the acsAbortStream() method.
// Request to abort an ACS stream. There is no confirmation event for this// method. RetCode_t nRetCode = acsAbortStream(acsHandle, // Handle of the active ACS stream. NULL // Private data not used, passing NULL );
Code Snippet 9: acsAbortStream() Code Example
2.5 DIFFERENCES BETWEEN THE ACSCLOSESTREAM() AND ACSABORTSTREAM() METHODS
While both acsCloseStream() and acsAbortStream() serve a similar purpose, there are some differences between
them. A list of these differences is presented below:
1. In the case of the acsAbortStream(), the ACS stream’s handle becomes invalid immediately after the
acsAbortStream() returns, while in the case of acsCloseStream(), the Stream’s handle remains valid until the
application receives a corresponding confirmation event for acsCloseStream().
avaya.com
18
2. In the case of acsCloseStream(), there is an associated confirmation event, while there is no associated
confirmation event for acsAbortStream().
3. In the case of the acsAbortStream(), the system frees all resources associated with the aborted ACS Stream
immediately, including any events queued on this stream, while in the case of acsCloseStream(), the application
will continue to receive events until it receives the confirmation event for the acsCloseStream() . Once the
confirmation event is received, the system will free all resources associated with the closed ACS Stream.
An application may use acsAbortStream() to unilaterally (and synchronously) terminate an ACS stream when:
• It does not require a confirmation of a successful stream closure, and,
• It does not need to receive any events that may be queued for it on that stream.
Section 3: Control Services and the Request/Response Model
This chapter describes in detail the control services provided by the TSAPI interface, supported event types and
the request and response framework.
3.1 CONTROL SERVICES
The TSAPI interface provides two types of controlling services:
• Application Programming Interface (API) Control Services (also referred to as ACS)
• CSTA control services
3.1.1 Application Programming Interface (API) Control Services
Applications use API Control Services (ACS) to manage the interactions with TSAPI Service running on the AE
Services server. These interactions primarily include services required for maintaining sessions and handling the
event queue maintained by the TSAPI Client library. The method names of the APIs providing these services start
with ‘acs‘, e.g., acsOpenStream() method.
Applications can use ACS functions to do the following:
• Get a list of available advertised services.
• Open an ACS stream with (or without) Private Data version negotiation.
• Close an ACS stream.
• Block or poll for events.
• Query an ACS stream for its service name (beginning with AE Services 4.1).
• Control the interval at which TSAPI Service sends heartbeat events to the client (beginning with AE Services 4.1).
avaya.com
19
3.1.2 CSTA Control Services
CSTA control services provide CSTA functionality for the TSAPI interface. These services are as defined in the
CSTA specifications and start with ‘csta’, as in the cstaMonitorDevice() method. Applications can use these services
only after opening an ACS stream.
The following services are available via CSTA control services:
• Call Control Services.
• Escape Services.
• Maintenance Services.
• Query Services.
• Routing Services.
• Set Feature Services.
• Snapshot Services.
• Status Services.
3.2 REQUEST/RESPONSE MODEL
Avaya’s TSAPI implementation of CSTA services follows a multi-step request and response model, and therefore
each CSTA service request results in an event sent back to the application asynchronously.
An application sends a request (or TSAPI requests), requesting TSAPI Service to take some action. The AE
Services server then processes each request. The server may take additional steps to pass the request to
Communication Manager and handle the response(s) before responding asynchronously to the application.
Synchronous response in the form of return code for the request merely indicates that the request was received
and is being processed. The actual request’s result is communicated back to the application in the form a
confirmation event later.
3.2.1 TSAPI Requests
After successfully opening an ACS stream, an application may request different CSTA services by sending TSAPI
requests. In each service request, the application passes the handle of the stream over which it is making the
request. Each service request passes an invokeID to distinguish it from the other service requests. The invokeID
provides a way to match the confirmation/failure event to the corresponding service request. An invokeID used by
the application must be unique so that the application can associate the response with the appropriate request.
3.2.2 TSAPI Responses
TSAPI Service sends a response to every request. This could be a positive acknowledgement in case of success
or a negative response in case of a failure. A TSAPI response is also referred to as the confirmation event sent by
TSAPI Service. See Chapter 4: for detailed information regarding event handling.
As an example, the cstaMakeCall() method is used here to illustrate the request/response model (Figure
3). When an application sends a request to make a call using the cstaMakeCall() method, it receives a
avaya.com
20
CSTAMakeCallConfEvent confirmation event as a response to the service request. In this example, the application is
using a library generated invokeID, and therefore the client library is responsible for generating a unique numeric
identifier (e.g., 123), representing invokeID, and return to the application.
Figure 3: TSAPI Application Request/Response
Section 4: Event Handling
This chapter explains the reader how TSAPI Service responds to requests sent by the TSAPI application using
client library. This chapter also describes the types of events supported, different ways to register the event handler
with the TSAPI Client library, and how to extract the event information from the event object.
Once the acsOpenStream() method returns successfully, the application should set up the event notification to
receive events and confirmation responses before requesting any other services. A TSAPI application typically
performs the following steps:
• Event Handler Registration: Setup the notification mechanism for receiving a notification when an event is
added in the TSAPI Client’s receive queue.
• Events Enablement: Start monitors to receive events for status changes.
• Retrieve Events: Retrieve events from the TSAPI Client’s receive queue using either acsGetEventBlock() or
acsGetEventPoll() method.
• Event Information Retrieval: Extract event information (including Private Data) from the received events.
avaya.com
21
4.1 TYPES OF EVENTS
In Avaya TSAPI, there are two types of event messages that are sent by AE Services server to the application:
1. UNSOLICITED events.
2. CONFIRMATION events.
A TSAPI application receives UNSOLICITED events in the form of ACSUNSOLICITED/CSTAUNSOLICITED events and
CONFIRMATION events in the form of ACSCONFIRMATION/CSTACONFIRMATION events.
1. UNSOLICITED events: UNSOLICITED events report state changes of various objects, such as call or device objects,
managed by Communication Manager. In addition to setting up a notification mechanism, applications need
to start a monitor on the desired object. Once the monitor is established, any changes to the object’s state are
reported to the monitoring application through these events.
Multiple unsolicited events could be received for a single monitor. For example, after a call monitor is started
by the application, events such as CSTA_ESTABLISHED, CSTA_HELD are sent to the application when the call state
changes.
2. CONFIRMATION events: A CONFIRMATION event is received as a response to a TSAPI request. There is only one
confirmation event sent for each TSAPI request. The application needs to setup the notification mechanism for
receiving these events. For example, TSAPI Service will send a CSTAMonitorConfEvent event to an application
after TSAPI Service has successfully processed the cstaMonitorDevice request.
The application must handle these events by setting up the appropriate notification mechanisms.
4.2 SETTING UP THE NOTIFICATION MECHANISM
The application should set up the notification mechanism to get notified about incoming events. There are
two mechanisms by which the application can be notified about the events: the Event Service Routine (ESR)
mechanism, and the acsEventNotify() method. In both approaches, the application is notified about the arrival of
a new event in the queue. Functions described in the next two sections do not remove the events from the event
queue. It is the application’s responsibility to retrieve the event from the event queue. Section 4.4 provides more
information on retrieving events.
4.2.1 The Event Service Routine (ESR) Mechanism
The application can use the ESR (Event Service Routine) mechanism to receive an asynchronous notification
of the arrival of an incoming event from TSAPI Service. The acsSetESR() method enables the notification of
incoming events via an application-defined callback method. This callback method will be invoked whenever
there is an event added in the TSAPI Client library queue. A pointer to the callback method is passed through
the esr parameter of thThe acsSetESR() method should be invoked once the acsOpenStream() method is returned
successfully before requesting any other services. Whenever the callback method is called, the application must
retrieve the events from the Client library’s event queue to prevent the queue from overflowing.
avaya.com
22
It is recommended that the application should be designed to have a separate thread (one different from the
callback method thread) for retrieving and processing the events. In cases where the application chooses to
receive a notification for each event whenever an event is added in the Client library’s event queue, the need to
retrieve and process the events in a separate thread becomes more evident. Retrieving and processing the events
in a separate thread ensures that the callback method’s thread is not blocked and remains free to receive further
notifications from the Client library.
4.2.1.1 acsSetESR Method
acsSetESR Method’s Signature:
typedef void (*EsrFunc)(unsigned long esrParam);
RetCode_t acsSetESR (ACSHandle_t acsHandle, /* INPUT */ EsrFunc esr, /* INPUT */ unsigned long esrParam, /* INPUT */ Boolean notifyAll /* INPUT */ );
Code Snippet 10: acsSetESR() Method’s Signature
acsSetESR Method’s Parameters:
• acsHandle - This is the value of the unique handle to the opened stream, for which this ESR routine will
apply. Only one ESR is allowed per active acsHandle.
• esr - This is a pointer to the ESR (the address of a method). The application can pass a NULL pointer to clear
an existing ESR.
• esrParam - This is a user-defined parameter which will be passed to the ESR when it is called.
• notifyAll - If this parameter is TRUE then the ESR will be called for every event. If it is FALSE then the ESR will
only be called each time the receiving queue becomes non-empty, i.e., the queue count changes from zero
(0) to one (1). This option may be used to reduce the overhead of notification.
acsSetESR Method’s Return Values:
A positive return value indicates success and a negative return value indicates failure. There is no confirmation
event for this method. The positive return value is:
• ACSPOSITIVE_ACK: The method completed successfully as requested by the application. No errors were detected.
Possible error is (negative return value):
• ACSERR_BADHDL: This indicates that the acsHandle being used is not a valid handle for an active ACS stream.
No changes occur in any existing streams if a bad handle is passed with this method.
acsSetESR Example Code:
The following code snippet shows a call to the acsSetESR() method. Refer to the OpenACSStream() and
ESRCallback() methods in Appendix B: Complete Example Code to see the complete procedure of calling the
acsSetESR() method, as well as how an application can implement the callback method.
avaya.com
23
// Register a callback method with client library to receive a notification when an// event is available in the Client library’s event queue. // Upon success, the Client library will call ESRCallback method whenever there is// an event available in its event queue.
nRetCode = acsSetESR(acsHandle, // handle returned by the acsOpenStream() method ESRCallback, // callback method defined by the application (unsigned long)acsHandle, // passing the ACS stream handle as // user defined parameter TRUE); // Setting TRUE to receive callback for each event added to // the client library’s event queue.
// check the return code if(nRetCode != ACSPOSITIVE_ACK) { cout<<" ERROR: acsSetESR() method return with an error.";
if(nRetCode == ACSERR_BADHDL) { cout<<" ulAcsHandle being used is not a valid handle"<<endl; }
else { cout << " acsSetESR() failed with unknown error. " << endl; cout << " Error code: " << nRetCode; } }
Code Snippet 11: acsSetESR() Code Example
4.2.2 The acsEventNotify() Method
The acsEventNotify() method is used to enable the notification for an incoming event via Windows messages.
This method allows an application to request that an application defined message be posted to the application’s
message queue when an incoming ACS or CSTA event is available. Once the application invokes this method with
the appropriate values, TSAPI Service starts sending the application defined message to the application’s message
queue with the appropriate event class and event type information. The acsEventNotify() method should be invoked
once the acsOpenStream() method is returned successfully before requesting any other services.
4.2.2.1 acsEventNotify Method
acsEventNotify Method’s Signature:
RetCode_t acsEventNotify (ACSHandle_t acsHandle, /* INPUT */ HWND hwnd, /* INPUT */ UINT msg, /* INPUT */ Boolean notifyAll /* INPUT */ );
Code Snippet 12: acsEventNotify() Method’s Signature
acsEventNotify Method’s Parameters:
• acsHandle - This is the value of the unique handle to the opened ACS stream for which event notification
messages will be posted.
avaya.com
24
• hwnd - This is the handle of the window which is to receive event notification messages. If this parameter is
NULL, event notification is disabled.
• msg - This is the user-defined message to be posted when an incoming event becomes available. The wParam and
lParam parameters of the message will contain information about the event class and event type respectively.
• notifyAll - If this parameter is TRUE then a message will be posted for every event. If it is FALSE then a
message will only be posted each time the receive queue becomes non-empty.
acsEventNotify Method’s Return Values:
A positive return value indicates success and a negative return value indicates failure. There is no confirmation
event for this method. The positive return value is:
• ACSPOSITIVE_ACK: The method completed successfully as requested by the application. No errors were detected.
Possible error is (negative return value):
• ACSERR_BADHDL: This indicates that the acsHandle provided is not a valid handle for an active ACS stream.
No changes occur in any existing streams if a bad handle is passed with this method.
avaya.com
25
acsEventNotify Example Code:
// A message identifier can be defined like this. #define WM_ACSEVENT WM_USER + 101 // Method to open an ACS stream bool OpenACSStream(ACSHandle_t acsHandle);
// <summary> // This method creates a window and display it. After window is displayed, this // method calls internal method to open an ACS stream. If the stream is // opened successfully, this method calls acsEventNotify() TSAPI method to register// for receiving a notification when when an incoming ACS event is available. // </summary>
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; // Set the handle of the window
// Create the main window. hWnd = CreateWindow( L"MainWClass", // name of window class L"Sample", // title-bar string WS_OVERLAPPEDWINDOW, // top-level window CW_USEDEFAULT, // default horizontal position CW_USEDEFAULT, // default vertical position CW_USEDEFAULT, // default width CW_USEDEFAULT, // default height (HWND) NULL, // no owner window (HMENU) NULL, // use class menu hInstance, // handle to application instance (LPVOID) NULL); // no window-creation data
if (!hWnd) {
// creation of new window failed return FALSE;
}
ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd);
// Once the Window is displayed, the application send a request for // opening the ACS Stream.
// Handle to ACS stream ACSHandle_t acsHandle;
// OpenACSStream method is an user-defined method that send a request to// open a ACS stream. It returns true when ACS Stream is opened successfully. // See example code for implementation of OpenACSStream()method. if( OpenACSStream(acsHandle) )
{ RetCode_t nRetCode; // To store the return code
nRetCode = acsEventNotify((ACSHandle_t)acsHandle, // Handle of // active ACS Stream hWnd, // window handle WM_ACSEVENT, // Message ID in winproc TRUE // TRUE will enable the message to be // posted for every event. );
// Verification for the positive response if(nRetCode != ACSPOSITIVE_ACK)
avaya.com
26
{ // ERROR in acsEventNotify if(nRetCode == ACSERR_BADHDL)
{ // acsHandle being used is not a valid handle.
} else
{ // acsEventNotify() failed with unknown error. // Error code: nRetCode
} return FALSE;
} else
{ // acsEventNotify call is successful.
} }
else {
// Error: Open Stream request failed. }
return TRUE; }
// Application’s Windows procedure that will be called whenever there is a// new message posted for any of the windows belonging to this application. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
// wParam contains an ACSHandle_t // HIWORD(lParam) contains an EventClass_t
EventClass_t eventClass = HIWORD(lParam); // LOWORD(lParam) contains an EventType_t
EventType_t eventType = LOWORD(lParam);
switch (message) {
case WM_CREATE: // other initialization, etc... break;
case WM_ACSEVENT: {
// User defined message; this message only indicates an incoming event is// available. Use acsGetEventBlock()/acsGetEventPoll( ) to actually // retrieve the complete event. // See Notify method for details on how can an application call
// acsGetEventBlock()/acsGetEventPoll() to retrieve an event from the // client queue.
cout << "Event class received is : " << eventClass; cout << "Event type received is : " << eventType; }
default: {
// Unhandled message received } } // End of switch
}// End of Method
Code Snippet 13: acsEventNotify() Code Example
The acsEventNotify() method enables a notification to be sent for an incoming event; extracting events from the
Client library’s event queue is the responsibility of the application. To do this, the application can use either
the acsGetEventBlock()or the acsGetEventPoll() method. Refer to Section 7.5 for an example of how to use the
acsGetEventBlock() and acsGetEventPoll() methods.
avaya.com
27
4.3 STARTING MONITORS FOR UNSOLICITED EVENTS
To receive unsolicited events, the application first needs to start a monitor on objects such as a call or a device. The term device
refers to both physical devices (stations, trunks, and so on) and logical devices (VDNs or ACD splits), that are controlled by
Avaya Communication Manager. Each device is characterized by a set of attributes. These attributes define the manner in which
an application may observe and manipulate a device. For more information, see Reference [1], Chapter 4 “CSTA Objects”.
When there is a change in the status of a monitored object, the application receives an unsolicited event. The
Avaya TSAPI Client API provides different methods which can be used to set monitors.
A TSAPI application can use the cstaMonitorCall() method to monitor a call, and cstaMonitorDevice() to monitor a
device. For more information on monitoring services, see Reference [1], Chapter 10.
The Code Snippet 14 shows the usage of the cstaMonitorDevice() method.
// MonitorDevice() : This method demonstrates the use of cstaMonitorDevice()// method which is used to monitor the device (Extension) // and to receive the events that arrive for the device after// monitor request acknowledged. void MonitorDevice(ACSHandle_t acsHandle) { DeviceID_t szDeviceID = "40011"; // device to be monitored
// Store the return code of the method RetCode_t nRetCode = 0;
CSTAMonitorFilter_t filter; // Store the Montor Filter setting
// Setting each fiter to 0 to receive all the events. filter.call = 0; filter.agent = 0; filter.feature = 0; filter.maintenance = 0; filter.privateFilter = 0;
nRetCode = cstaMonitorDevice(acsHandle, // ACS Stream handle 0, // Invoke ID is ignored, as it is library generated. &szDeviceID, // ID of the device to be monitored &filter, // Filter setting that will apply on monitor. NULL // Private data not passed with this request );
if(nRetCode < 0) { cout<<" Failed to monitor device ID: "<< szDeviceID <<endl; cout<<" Error code: " << nRetCode; }
else {
// cstaMonitorDevice returned successfully // InvokeID generated for this method will be returned in nRetCode, // applications can store this invokeID in any data structure for later// comparison. // See Complete Example Code to see saving and comparing invokeID.
}
}// End of method.
Code Snippet 14: cstaMonitorDevice() Code Example
avaya.com
28
A confirmation event (i.e. CSTAMonitorConfEvent) is provided for each monitoring request. The CSTAMonitorConfEvent
event provides a monitor cross reference ID that the application can use to correlate the subsequent event reports
to the monitor request. Refer to Chapter 7: for an example of how to handle the CSTAMonitorConfEvent event.
4.4 RETRIEVING EVENTS
The TSAPI Client library stores all the events received from the TSAPI Service in a separate queue. To retrieve
these events from the Client library queue, the application must use one of the two event retrieval methods
provided by Avaya’s TSAPI implementation: blocking mode or non-blocking mode.
4.4.1 Blocking Mode
An application can use acsGetEventBlock()method to retrieve an event in blocking mode. In the blocking mode, the
application’s calling thread will be blocked until there is an event for the ACS stream that matches the acsHandle.
If acsHandle is set to zero, the application’s calling thread will block until there is an event for any ACS stream
opened by the application.
4.4.1.1 acsGetEventBlock Method
acsGetEventBlock Method’s Signature:
RetCode_t acsGetEventBlock ( ACSHandle_t acsHandle, /* INPUT */ void* eventBuf, /* OUTPUT */ unsigned short* eventBufSize, /* INPUT/OUTPUT */ PrivateData_t* privateData, /* OUTPUT */ unsigned short* numEvents /* OUTPUT */ );
Code Snippet 15: acsGetEventBlock() Method’s Signature
acsGetEventBlock Method’s Parameters:
• acsHandle - This is the value of the unique handle to the opened ACS stream. If a handle of zero (0) is given,
then the next message on any of the open ACS streams for this application is returned.
• eventBuf - This is a pointer to an area in the application address space large enough to hold one incoming
event. This buffer should be large enough to hold the largest event the application expects to receive.
Typically the application will reserve a space large enough to hold a CSTAEvent_t.
• eventBufSize - This parameter indicates the size of the user buffer pointed to by eventBuf. If the event is
larger than eventBuf, then this parameter will be returned with the size of the buffer required to receive the
event. The application should allocate a larger buffer and call the acsGetEventBlock() method again with a
pointer to the larger buffer.
• privateData - This parameter points to a buffer which will receive any Private Data that accompanies this
event. The length field of the PrivateData_t structure must be set to the size of the data buffer. If the
application does not wish to receive Private Data, then a NULL privateData pointer should be passed.
avaya.com
29
• numEvents - The library will return the number of events queued for the application on the ACS stream whose
handle is passed in the acsHandle parameter (not including the current event) via the numEvents parameter. In the
case when there are more than one streams opened by the application, and acsGetEventBlock() method is called to
get an event from any of the opened streams by setting the first parameter (i.e. acsHandle) to zero (0), the value of
numEvents will reflect the number of events remaining on the stream from which the event was actually received.
When the TSAPI Client library receives the acsGetEventBlock()call, it will copy the event from its event queue
into the eventBuf parameter. The eventBuf parameter specifies the buffer to store the incoming CSTA or ACS
event. The buffer should be large enough to hold the largest event the application expects to receive.
The application can specify the size of the event buffer by using the eventBufSize parameter of this method.
Typically, the application will reserve a space large enough to hold a CSTAEvent_t event. This method returns
ACSPOSITIVE_ACK if the method completed successfully and the event copied to the application’s data space.
acsGetEventBlock Method’s Return Values:
A positive return value indicates success and a negative return value indicates failure. There is no confirmation
event for this method. The positive return value is:
• ACSPOSITIVE_ACK: The method completed successfully as requested by the application, and an event has
been copied to the application data space. No errors were detected.
Possible errors are (negative return value):
• ACSERR_BADHDL: This indicates that the acsHandle being used is not a valid handle for an active ACS stream.
No changes occur in any existing streams if a bad handle is passed with this method.
• ACSERR_UBUFSMALL: The user buffer size indicated in the eventBufSize parameter was smaller than the
size of the next available event for the application on the ACS stream. The application should call the
acsGetEventBlock() method again with a larger buffer to retrieve the event that is still present in the Client
Library queue.
acsGetEventBlock Example Code:
Refer to Code Snippet 17 for usage of the acsGetEventBlock() method.
4.4.2 Non-Blocking Mode
An application can use acsGetEventPoll() method to receive an event in non-blocking mode. In the non-blocking
mode, the oldest outstanding event for the specified active ACS stream or for any active ACS stream (in case
acsHandle is set to 0), will be copied into the application’s data space and control will be returned to the
application. If no events are currently queued for the application, the method will return control immediately to the
application with a return code indicating that no events were available.
The incoming event received by the application is passed by using the eventBuf parameter of the acsGetEventPoll()
method. This parameter specifies the buffer to hold the incoming event (CSTA or ACS).
avaya.com
30
The buffer should be large enough to hold the largest event the application expects to receive. The application can
specify the size of the event buffer by using the eventBufSize parameter of this method. Typically the application
will reserve a space large enough to hold a CSTAEvent_t event.
4.4.2.1 acsGetEventPoll Method’s Signature:
acsGetEventPoll Method’s Signature:
RetCode_t acsGetEventPoll ( ACSHandle_t acsHandle, /* INPUT */ void* eventBuf, /* OUTPUT */ unsigned short* eventBufSize, /* INPUT/OUTPUT */ PrivateData_t* privateData, /* OUTPUT */ unsigned short* numEvents /* OUTPUT */ )
Code Snippet 16: acsGetEventPoll() Method’s Signature
acsGetEventPoll Method’s Parameters:
The acsGetEventPoll() method’s parameter are same as acsGetEventBlock() method. Refer to Section 4.4.1 for
detailed description of the parameters.
acsGetEventPoll Method’s Return Values:
The acsGetEventPoll()method return values are also same as return value of acsGetEventBlock() method (see
Section 4.4.1), with one exception:
• ACSERR_NOMESSAGE: There were no events available to return to the application.
acsGetEventPoll Example Code:
The following code snippet shows a call to the acsGetEventBlock() and acsGetEventPoll() methods. Refer
to the Notify() method in Appendix B: Complete Example Code for the complete procedure of calling the
acsGetEventBlock() and acsGetEventPoll() methods. See Chapter 7: to see event handling for other event handling
for other events.
avaya.com
31
// BLOCKING_MODE can be defined in the project settings > C/C++ > // Preprocessor to use acsGetEventBlock() method. If application // wants to use acsGetEventPoll method instead, exclude BLOCKING_MODE // from project setting.
#ifdef BLOCKING_MODE { // Retrieving the events in Blocking mode // If no events are currently queued for the applcation then this method // will wait blocking the application's calling thread until an event // becomes available.
RetCode_t nRetCode = acsGetEventBlock(acsHandle, // Handle of active ACS // Stream. (void *)cstaEvent, // Event buffer &usEventBufSize, // size of event buffer (PrivateData_t *)&privateData, // private Data. &usNumEvents // count of events pending in queue );
} #else { // Retrieving events in Non-Blocking mode // If no events are currently queued for the application, this method // will return immediately with an error code ACSERR_NOMESSAGE // indicating that no events were available. RetCode_t nRetCode = acsGetEventPoll(acsHandle, (void *)cstaEvent, &usEventBufSize, (PrivateData_t *)&privateData, &usNumEvents); } #endif
if(nRetCode != ACSPOSITIVE_ACK) {
if(nRetCode == ACSERR_BADHDL) { cout<<" The ACS Handle is invalid"<<endl<<endl; } // end of if
else if (nRetCode == ACSERR_UBUFSMALL) { cout<<" Passed event buffer size is smaller than the size of the"
" next available event for this ACS Stream."<<endl<<endl;
// The usEventBufSize variable has been reset by the TSAPI Client// Library to the size of the next message on the ACS// stream. The application should call acsGetEventBlock( )// again with a larger buffer. The ACS event still present// on the Client Library queue.
avaya.com
32
}// end of else if else if (nRetCode == ACSERR_NOMESSAGE)
{ // The acsGetEventPoll()method return this value to indicate // there were no events available in the Client library queue.
cout << " No events available at this time."; }
else { cout << " acsGetEventBlock()/acsGetEventPoll() failed with"
" unknown error. " << endl; cout << " Error code: " << nRetCode;
break; } }// end of if
else {
// We have successfully retrieved event, write event processing code here.}
Code Snippet 17: acsGetEventBlock() and acsGetEventPoll() Code Example
4.5 EXTRACTING EVENT INFORMATION
After receiving an event, the application should first check the event class and event type. The events received
from AE Services server are classified as either CSTACONFIRMATION/ACSCONFIRMATION (CONFIRMATION events) or
CSTAUNSOLICITED/ACSUNSOLICITED (UNSOLCITIED events).
Depending upon the event class and event type, the application should use appropriate event data structures to extract related
information. For example, when an application receives the DELIVERED event for a monitored object, the event class would be
CSTAUNSOLICITED and event type would be CSTA_DELIVERED. In this case, the application needs to use the CSTADeliveredEvent_t
event structure to extract information such as alerting device, calling device, etc. as shown in Code Snippet 18.
avaya.com
33
void EventHanlder(CSTAEvent_t* cstaEvent) {
switch(cstaEvent->eventHeader.eventClass) {
case CSTAUNSOLICITED: {
switch(cstaEvent->eventHeader.eventType) {
case CSTA_DELIVERED: {
// Extract information included in this event here // Copy the Delivered Event.
CSTADeliveredEvent_t deliveredEvent = cstaEvent->event.cstaUnsolicited.u.delivered;
// Extract calling device char* callingDevice = deliveredEvent.callingDevice.deviceID;
cout << endl << " A Delivered event is received For device " << callingDevice << endl;
break; } // End of Case
// Add cases to handle other unsolicited events as per the application needs.
default: {
// Other application should add more cases as per need. cout << " An unhandled unsolicited event received."; cout<<" Event Type: "<< cstaEvent->eventHeader.eventType; } } // End of switch
break; } // End of Case
case ACSCONFIRMATION: {
// Extract information included in this event here break;
} // End of Case default:
{ cout << " Received event with unknown event class."; cout<<" Event Class: "<< cstaEvent->eventHeader.eventClass; } }// End of eventClass switch
}// End of method
Code Snippet 18: Extracting Event Information Code Example
Note:
Code Snippet 18: Extracting Event Information Code Example
Note: Code Snippet 18 shows how to retrieve information from the passed event object. For some events, there may
be some Private Data associated with the event. Section 5.3 describes how to extract the Private Data parameter
from an event. Refer the Notify() method in Appendix B: Complete Example Code for code to handle other
applicable cases.
Section 5: Private Data
This chapter provides detailed information about the Private Data parameter. Private Data is the means for both
extending the functionality of any defined CSTA service and for providing additional functionality altogether.
avaya.com
34
TSAPI Service uses the “Private Data” mechanism to provide applications with access to special features of Avaya
Communication Manager.
Private Data may be defined for each CSTA service request, CSTA confirmation event, and CSTA unsolicited event.
Private Data allows a PBX or switch manufacturer to extend the base set of TSAPI capabilities. Over time, a PBX
manufacturer may choose to further enhance the capabilities that are available using Private Data.
A Private Data version defines a fixed set of features that are supported in the version. More specifically, it defines
a set of escape services and private event parameters for CSTA events. This lets the application developer know
exactly which services and Private Data items are available in a specific version. Having the ability to negotiate a
specific Private Data version ensures that an application written for an earlier release of AE Services will continue
to operate with newer releases.
To see the list of features supported in different Private Data versions, see Reference [1], Appendix B, “Summary
of Private data support”.
5.1 PRIVATE DATA NEGOTIATION
Any client application can use Private Data to access special features of Avaya Communication Manager. A special
feature means a set of information to achieve certain functionality, such as “Single Step Transfer Call”, “Calling
Device in Failed Event”, “Network Call Redirection for Routing” etc.
A PBX or switch manufacturer can add new functionality that can be accessed using Private Data; when they do
so, the Private Data version is set to a higher one than the previous version. To be able to use any special feature,
the application needs to know the Private Data version that contains that special feature. A particular Private Data
version is backward compatible with a previous one, i.e., it supports the entire set of features supported by an
earlier version.
To access any special features, the application needs to negotiate the correct Private Data version with TSAPI
Service. This is done at the time of opening an ACS stream. After the negotiation, the Private Data version remains
the same for all the requests and responses for this stream.
To negotiate Private Data Version (PDV), the acsOpenStream() method must be called with the privateData parameter
which is of type ATTPrivateData_t structure. This structure must contain the correctly formatted information to
negotiate PDV with which the application is compatible. An application that does not use Private Data should pass
NULL in privateData parameter at the time of calling the acsOpenStream() method.
The following data must be passed as the negotiation information in the privateData parameter in the
acsOpenStream() method:
• vendor: To indicate that Private Data negotiation is intended, the application sets the vendor field in the
PrivateData structure to the null terminated string “VERSION”.
• data: The application specifies the acceptable version(s) in the data field of ATTPrivateData_t structure. The
data field contains a constant PRIVATE_DATA_ENCODING followed by the null terminated ACSII string containing
the version or range of versions. The version string should be passed as formatted. The AE Services TSAPI
avaya.com
35
SDK provides the attMakeVersionString() method to simplify formatting of the requested version string. The
application is required to format the version string using the attMakeVersionString() method.
• length: The length parameter of ATTPrivateData_t structure needs to be set appropriately to the total length of the
private data structure.
Note 5, Private Data Version Negotiation provides more information on the PDV negotiation process.
Code Snippet 19: Passing Private Data Negotiation Information Code Example illustrates the process to prepare
Private Data negotiation information to be sent in acsOpenStream() method. Refer to the OpenACSStream() method
in Appendix B: Complete Example Code for the complete procedure of preparing and sending Private Data
negotiation information in acsOpenStream() method.
// Below code demonstrate the process to send private data version// negotiation information in the acsOpenStream() method.// The application first set the vendor field of private data buffer // to VERSION and then prepare a ASCII string identifying the version // of the private data that the application wish to work with.
// ATT service request private data buffer ATTPrivateData_t privateData;
// privateData.vendor: should be set to 'VERSION' as follows strcpy_s(privateData.vendor, "VERSION");
// privateData.data: should be set to a one byte discriminator// PRIVATE_DATA_ENCODING followed by an ASCII string identifying// the version of the private dataprivateData.data[0] = PRIVATE_DATA_ENCODING;
avaya.com
36
// A special function is used to convert version string into the format// required by the acsOpenStream function.
// Contain private data version that is requested by user // for negotiation.Version_t szPrivateDataVersion;
cout << endl << " Please enter private data version: ";cin >> szPrivateDataVersion;
if((attMakeVersionString(szPrivateDataVersion, &(privateData.data[1]))) > 0 ) {
// Adding 2 extra bytes here, one byte for element at zero location// (i.e. data[0] as it is one byte long) // and one byte for trailing null (as the ASCII string will be// null terminated).
// Set the privateData length privateData.length = (unsigned short) strlen(&privateData.data[1]) + 2;}else{
// attMakeVersionString failed due to incorrect version passed. cout << endl << "attMakeVersionString() method failed..."; cout << endl << "If you want to continue, a default Private Data"
" Version string (3-8) will be used." " Press y/Y to continue or any other key to exit the "
" application... ";char cContinue = _getche();
if(cContinue == 'Y' || cContinue == 'y') {
// As this application make use of private data, setting PDV to a// default value "3-8" in this case.
strcpy_s(szPrivateDataVersion, "3-8");
// Calling attMakeVersionString again with default PDV if ( (attMakeVersionString(szPrivateDataVersion, &(privateData.data[1])))
> 0 ) {
// Adding 2 extra bytes here, one byte for element at zero location// (i.e. data[0] as it is one byte long) // and one byte for trailing null (as the ASCII string will be// null terminated).
// Set the privateData length privateData.length = (unsigned short) strlen(&privateData.data[1]) + 2; }
else {
// This case should not happen as such exit(-1); } }
else {
// Exit as user do not want to continue. exit(-1); } }// privateData buffer contains required private data negotiation information.// privateData buffer can be passed in acsOpenStream() method as demonstrated// in
Code Snippet 19: Passing Private Data Negotiation Information Code Example
avaya.com
37
After sending a TSAPI Service request for negotiating Private Data in the acsOpenStream() method, the TSAPI
application should verify that the Private Data information in the confirmation event of acsOpenStream (i.e., in
ACSOpenStreamConfEvent) received from the AE Service server as outlined below.
• The length field of Private Data received should be greater than 0.
• The ‘vendor’ field of Private Data should be set to “ECS”.
• The first byte of the ‘data’ field of Private Data should be a one byte discriminator PRIVATE_DATA_ENCODING.
• Additionally, ‘data’ field should contain the Private Data version (PDV) number as requested in the
acsOpenStream() method.
Code Snippet 20: Negotiating Private Data Code Example illustrates the negotiation of Private Data. Refer to the
Notify() method in Appendix B: Complete Example Code for the complete procedure of verifying that private data is
negotiated correctly.
// CheckPrivateDataVersion: This method demonstrates how an application can verify// that private data version is negotiated correctly. This method only handles// ACS_OPEN_STREAM_CONF event for demonstration purpose. Refer Notify() method // in complete example code for full implementation. // @acsHandle: Pass handle to active ACS stream.// @a_pCstaEvent: Pass CSTA Event object that was retrieved using either // acsGetEventBlock() or acsGetEventPoll() method. // @a_pPrivateData: Pass private data buffer pointer that was returned // by either acsGetEventBlock() or acsGetEventPoll() method. // @a_nOpenStreamInvokeID: Pass invokeID value returned by acsOpenStream() method. // @a_pPDVRequested: Pass private data version that user has requested while // calling acsOpenStream() method. void CheckPrivateDataVersion(ACSHandle_t acsHandle, CSTAEvent_t* a_pCstaEvent, ATTPrivateData_t* a_pPrivateData,
int a_nOpenStreamInvokeID,
avaya.com
38
char* a_pPDVRequested) {
// Checking for Confirmation event for the Open Stream request switch(a_pCstaEvent->eventHeader.eventClass)
{ case ACSCONFIRMATION:
{ switch(a_pCstaEvent->eventHeader.eventType)
{ case ACS_OPEN_STREAM_CONF:
{ if(a_nOpenStreamInvokeID ==
a_pCstaEvent->event.acsConfirmation.invokeID) { cout << endl <<" acsOpenStremConfEvent received - Stream"
" opened successfully."<<endl; cout << " API Version: " << a_pCstaEvent->event.acsConfirmation.u.acsopen.apiVer << endl; cout << " Library Version: " << a_pCstaEvent->event.acsConfirmation.u.acsopen.libVer << endl << endl;
// verify that Private Data is correctly negotiated.
// 1st check the length of the Private Data received if (a_pPrivateData->length <= 0)
{ cout << endl << " Private Data length is zero"
" in acsOpenStreamConf event. Private data" " is not sent as a part of this event.";
// As this application is making use of private// data features, in this condition it is// appropriate to close the application as the // private data is not negotiated successfully. // This could happen if you send a PDV which is // not in range of PDV that AE Services server // supports e.g. "10-25", "0-5", "0", "10" etc
// hanlde error condtion ( abort the Stream ) // return error
// If we do not close application and use PD // feature, the request in which we are using // PD feature will fail. In this example,// cstaMonitorDevice method will fail.
}
// 2nd Check the vendor String
if ( strcmp(a_pPrivateData->vendor,ECS_VENDOR_STRING) != 0 ) {
// hanlde error condtion ( abort the Stream ) // return error
}
// 3rd check the One byte descriminator if ( a_pPrivateData->data[0] != PRIVATE_DATA_ENCODING )
{ // handle error condtion ( abort the Stream ) // return error
} else
{
avaya.com
39
// Retrieving the Private Data cout <<" PrivateData = VENDOR: "<< a_pPrivateData->vendor << endl;
// Checking private data version, whether // it is same as requested or not. char cPDVReturned = a_pPrivateData->data[1];
// To hold returned PDV as number int nReturnedPDV = atoi(&cPDVReturned);
if(strchr(a_pPDVRequested, '-') == NULL) {
// Requested version is specific i.e. does not contain '-' int nRequestedPDV = atoi(a_pPDVRequested);
if(nRequestedPDV == nReturnedPDV) { cout <<" Private data version negotiation is "
"successful."; cout <<" Negotiated private data version is: " << cPDVReturned << endl; }
else { cout <<" Private data version negotiation failed.";
// This is an error condition where AE Server does // not support the PDV requested.
} }
else {
// A range of PDV is requested
// Make a copy of requested PDV value, this // variable will also store first part of requested PDV // after call to strtok_s. char* szFirst = _strdup(a_pPDVRequested); // To store second part of requested PDV char* szSecond;
// strtok_s method is used to split string// which is of format "m-n". This method // will copy m in szFirst and n in szSecond.
strtok_s(szFirst, "-", &szSecond);
int nMinVersion = atoi(szFirst); int nMaxVersion = atoi(szSecond);
// compare value of m and n with // PDV returned. if(nReturnedPDV >= nMinVersion
&& nReturnedPDV <= nMaxVersion ) { cout <<" Private data version negotiation is "
"successful."; cout <<" Negotiated private data version is: " << cPDVReturned << endl; }
else { cout <<" Private data version negotiation failed.";
avaya.com
40
// This is an error condition where AE Server does // not support the PDV requested.
} }// End of else }// End of else } // End of invokeID if } // End of ACS_OPEN_STREAM_CONF case } // End of eventType Switch } // End of case } // End of eventClass Switch } // End of method
Code Snippet 20: Negotiating Private Data Code Example
5.2 Sending a Request Containing Private Data Once Private Data has been negotiated correctly, the application can send the Private Data in the TSAPI request to the TSAPI server.
For example, in case the TSAPI application needs to set the Agent’s work mode, it can be done using the attV6SetAgentState() method. This method encodes the work mode in Private Data. After the work mode is encoded using the attV6SetAgentState() method, Private Data can be used in the cstaSetAgentState() method to set the Agent’s work mode.
The code snippet below shows how to encode the Agent’s work mode and pass it into the cstaSetAgentState() method to set the Agent’s work mode.
Code Snippet 20: Negotiating Private Data Code Example
5.2 SENDING A REQUEST CONTAINING PRIVATE DATA
Once Private Data has been negotiated correctly, the application can send the Private Data in the TSAPI request to
the TSAPI server.
For example, in case the TSAPI application needs to set the Agent’s work mode, it can be done using the
attV6SetAgentState() method. This method encodes the work mode in Private Data. After the work mode is encoded
using the attV6SetAgentState() method, Private Data can be used in the cstaSetAgentState() method to set the
Agent’s work mode.
Code snippet 21 shows how to encode the Agent’s work mode and pass it into the cstaSetAgentState() method to
set the Agent’s work mode.
// SetAgentState: This method demonstrates use of attV6SetAgentState()// method to encode the Agent’s work mode. This method also demonstrates // use of cstaSetAgentState() method to set the Agent’s work mode. // @acsHandle: Pass handle to active ACS stream.bool SetAgentState(ACSHandle_t acsHandle) { InvokeID_t ulinvokeID; // APP_GEN/LIB_GEN DeviceID_t szDeviceID = "40010"; // Agent Terminal PrivateData_t privateData; // Privatedata
long lReasonCode = 1; // Reason code single digit 1-9 AgentMode_t enumAgentMode; // the mode in which Agent logs in AgentPassword_t szAgentPassword =""; // Agent Password AgentID_t szAgentID = "50001"; // Store the AgentID AgentGroup_t szAgentGroup; // Hunt Group Extension (optional) ATTWorkMode_t enumWorkMode; // “AutoIn”/”ManualIn” etc RetCode_t nRetCode; // return value from the API
enumAgentMode = AM_LOG_IN; // Requested Agent Mode,// allows agent to log in
avaya.com
41
// attV6SetAgentState(privatedata, workMode, reasoncode,enablePending) // this method is used here to set the work mode for Agent. // work mode is g3 Private Data variable, thus need to be set using this // method. V6 method is used as this Structure is retained for further// PDV environment (i.e. PDV 7 and PDV 8) for setting Agent work mode.
enumWorkMode = WM_AUTO_IN; // for “AutoIn mode” // enumWorkMode = WM_MANUAL_IN; // Use for “Manual In mode”
nRetCode = attV6SetAgentState((ATTPrivateData_t *)&privateData, enumWorkMode, lReasonCode, FALSE // To enable pending );
if (nRetCode < 0) {
// Error encoding private datareturn false;
}
// Using privateData that contains encoded Agent’s work mode nRetCode = cstaSetAgentState(acsHandle, (InvokeID_t)ulinvokeID, (DeviceID_t *)szDeviceID, enumAgentMode, (AgentID_t *)&szAgentID, (AgentGroup_t *)&szAgentGroup, (AgentPassword_t *) &szAgentPassword, (PrivateData_t *)&privateData );
if (nRetCode < 0) {
// error setting the agent state cout<<" Error while setting Agent State " <<endl; cout<<" Error Return Code: " <<nRetCode;
// handle error return false;
} return true;
}// End of method
Code Snippet 21: Sending Private Data Request Code Example
5.3 Retrieving Private Data from an Event When an event, either CONFIRMATION or UNSOLICITED, is retrieved from the TSAPI Client library queue using either the acsGetEventBlock() or the acsGetEventPoll() methods, the application can use the following steps to extract Private Data information from the event:
1) The application should first check the length of the Private Data returned.
2) The application should then pass the Private Data to the attPrivateData() method for decoding. This method on success will return the Private Data in a structure defined as ATTEvent_t.
3) The application should check the private event type and extract the information.
Code Snippet 21: Sending Private Data Request Code Example
5.3 RETRIEVING PRIVATE DATA FROM AN EVENT
When an event, either CONFIRMATION or UNSOLICITED, is retrieved from the TSAPI Client library queue using either
the acsGetEventBlock() or the acsGetEventPoll() methods, the application can use the following steps to extract
Private Data information from the event:
1. The application should first check the length of the Private Data returned.
2. The application should then pass the Private Data to the attPrivateData() method for decoding. This method on
success will return the Private Data in a structure defined as ATTEvent_t.
3. The application should check the private event type and extract the information.
Please note that for an ACS confirmation event, the Private Data information is not encoded hence the application
only need to check the length of the Private Data before retrieving Private Data information. Code Snippet 20:
avaya.com
42
Negotiating Private Data Code Example demonstrates extracting the Vendor and Private Data version parameters
from the ACS_OPEN_STREAM_CONF (ACS Confirmation) event.
In case when an application receives a CSTA CONFIRMATION or UNSOLICITED event, the application needs to follow
all the three steps as mentioned above.
A sample code snippet to demonstrate how an application can decode and extract private data information from
the CSTA_DELIVERED (CSTA UNSOLICITED) event is given below:
// <summary> // This method demonstrates the procedure to extract the private data// information from delivered event (UNSOLICITED). // </summary> // <param name="cstaEvent"> Pointer to CSTAEvent_t object that contains event’s // information</param> // <param name="privateData"> Pointer to ATTPrivateData_t object that contains // private data information.</param> void ExtractPrivateDataInformation(CSTAEvent_t* cstaEvent, ATTPrivateData_t* privateData){
// Check to event type. switch(cstaEvent->eventHeader.eventType)
{ case CSTA_DELIVERED:
{ // Delivered event received
LocalConnectionState_t connectionState; // To store the connection state connectionState = cstaEvent->event.cstaUnsolicited.u.delivered. localConnectionInfo; CSTAEventCause_t eventCause; // To store the event cause eventCause = cstaEvent->event.cstaUnsolicited.u.delivered.cause;
// check the connection state and cause for the event if( connectionState == csAlerting && eventCause == ecNewCall )
{ // Retrieving the information associated with this event long lcallID = cstaEvent->event.cstaUnsolicited.u.delivered.
connection.callID; char* szCallingDeviceID = cstaEvent->event.cstaUnsolicited.u.
delivered.callingDevice.deviceID; char* alertingDevice = cstaEvent->event.cstaUnsolicited.u.
delivered.alertingDevice.deviceID;
cout<<" An incoming Call with CallID "<<lcallID<<" received" <<" from "<<szCallingDeviceID<<" to " << alertingDevice << endl;
// check the privateData length if ( privateData->length > 0 )
{ // Event buffer that will contain the decoded private data information.
ATTEvent_t attEvent;
avaya.com
43
// Check to ensure that private data is successfully decoded. if ( attPrivateData(privateData, &attEvent) == ACSPOSITIVE_ACK )
{ // check the event type if ( attEvent.eventType == ATT_DELIVERED )
{ // This sample code snippet shows how to // extract UCID information from private data, // other application can similarily extract other private data // information as required.
// extract the Universal Call Identifier (UCID) char* UCID = attEvent.u.deliveredEvent.ucid;
cout << " The UCID is: " << UCID; } // End of if } // End of if
else {
// Decoding Error. cout << " An error occured while decoding"
" private data." << endl; } }// End of outer if
else {
// The event does not contain any private Data. } } // End of if
break; }// End of case
// Application can add other cases for processing other events as needed. }// End of switch }// End of method
Code Snippet 22: Private Data Information Extraction Code Example
Section 6: Setting Up the TSAPI Programming Environment
This chapter examines the TSAPI Client and TSAPI SDK contents along with changes required in the configuration file.
To set the TSAPI programming environment, it is required to install the TSAPI Client and the SDK and also make
some changes in the configuration file. The TSAPI Client is required for executing a TSAPI application while the
TSAPI SDK is required for developing a TSAPI application.
Following are the basic steps to set up the TSAPI environment:
• Installing the TSAPI Client.
• Installing the TSAPI SDK.
• Changes to be made in the configuration file i.e., TSLIB.INI.
Please refer to reference [2] for more information on installing the TSAPI Client and the SDK.
avaya.com
44
Note: The TSAPI SDK and TSAPI Client may be obtained via the Avaya DevConnect Program for either Windows
32-based operating systems, or as an OS-independent version used for Linux development. The TSAPI Client is
available for download from the DevConnect portal. The TSAPI SDK is orderable via the DevConnect procurement
benefits (or authorized Avaya BusinessPartners).
The following sub-sections present a description of the components and contents that are copied on the system
while installing the TSAPI Client and SDK.
6.1 TSAPI CLIENT COMPONENTS
The TSAPI Client provides applications access to Avaya Communication Manager call processing. The primary
component of the TSAPI Client is the TSAPI library. The TSAPI library is the C/C++ library of function calls that
enables an application to request different services provided by TSAPI.
Additionally, the TSAPI Client provides access to Avaya Private Data. Avaya Private Data provides access to
specialized features of Avaya Communication Manager.
The TSAPI Client interacts with TSAPI Service running on AE Services server. AE Services server interacts with
Avaya Communication Manager to provide call processing services. The TSAPI Client must be installed on the
machine for a TSAPI application to run in an AE Services/Communication Manager environment.
The following components are available after the TSAPI Client is installed:
• TSLIB.INI: This configuration file contains a list of the AE Services servers offering telephony services via TCP/IP.
Either Fully Qualified Domain Name (FQDN) or IP address of AE Services server can be used in the list. Instead of
each workstation maintaining its own list of servers, a shared tslib.ini file can be placed on a network file system.
• OpenSSL License: This file contains information about the terms of the license.
• TSAPI Client Readme: This file contains updated information about the TSAPI Client.
• TSAPI Spy: This application can be used to obtain a trace of messages flowing between applications and TSAPI
Service. This tool can be helpful in troubleshooting communication issues between the TSAPI Client library and
AE Services server.
• TSAPI Test: This is a TSAPI test application which can make call from one extension to another. This application
is useful to verify whether the TSAPI Client setup is correctly configured.
6.2 TSAPI SDK COMPONENTS
The AE Services Telephony Services API (TSAPI) SDK is available to developers to help them in developing the
applications that access the AE Services’ TSAPI Service. The TSAPI Client is a prerequisite for installing and using
the TSAPI SDK – developers must install the TSAPI Client first.
The TSAPI SDK components that are installed by default are:
• Sample Code: This directory contains sample code for developing various applications using Avaya TSAPI.
• ReadMe file: This file contains updated information about the Avaya TSAPI SDK.
avaya.com
45
• TSAPI Exerciser: The Application Enablement (AE) Services TSAPI Exerciser is a Windows application that
enables the user to invoke TSAPI methods interactively. The TSAPI Exerciser is meant to be used to help TSAPI
application developers discover how to use the API to access information and invoke requests.
• Apart from this, headers and libraries are also installed in the same folder where the sample code resides.
6.2.1 TSAPI SDK Header Files
The TSAPI SDK header files contain coding structures needed for designing and maintaining the applications. For
designing and updating the application with Private Data, the attpriv.h and attpdefs.h header files are used.
• attpriv.h: The attpriv.h file contains the Protocol Data Units (PDU) structures for the PDUs that are defined in the
attpdefs.h file.
• attpdefs.h: The attpdefs.h file contains the definitions of the Protocol Data Units (PDUs) that are used for Private
Data version control. Each PDU in the attpdefs.h file has a PDU number associated with it. The PDU numbers
with the highest values represent the latest version of Private Data for a given service, confirmation event, or
unsolicited event.
6.2.2 TSAPI Service Client Libraries
The TSAPI Service client libraries provide a set of functions that act as an interface between itself and the client
applications (Table 3). Applications use these functions to establish an authorized connection with TSAPI Service
and to send telephony control messages (CSTA messages) to Avaya Communication Manager. The TSAPI SDK
library files are import libraries.
Library Name DescriptionCSTA32.DLL Contains TSAPI functions (CSTAServiceName) and the ACS Service name services.
ATTPRV32.DLL Contains Private Data encoding and functions (ATTServiceName).
Table 3 : TSAPI Client libraries from the Win32 TSAPI SDK
6.3 CHANGES TO THE FILE TSLIB.INIOnce the TSAPI Client is installed, developers also need to change the TSLIB.INI file (File 1) to use the correct
AE Services’ Fully Qualified Domain Name or IP address and port number. The following parameters need to be
changed to accomplish this:
• Telephony Servers: This is a mandatory parameter and should be set to the Fully Qualified Domain Name or IP
address of the AE Services server.
• Shared Admin: This section is for sharing a single copy of the TSLIB.INI file among all the clients. This section
contains a pointer to the server configuration file that contains the Fully Qualified Domain Name or IP address
of the AE Services server.
The TSLIB.INI file is located in the <Installation Path>/AE Services/TSAPI Client subdirectory.
avaya.com
46
[Telephony Servers] ; This is a list of the servers offering Telephony Services via TCP/IP. ; Either domain name or IP address may be used; default port number is 450 ; The form is: host_name=port_number. ; For example: ; tserver.mydomain.com=450 ; 127.0.0.1=450
192.168.17.128=450
; The entry above indicates that the TSAPI Client can connect to a machine on ; port 450 and IP address of machine is 192.168.17.128. This is the machine on which; TSAPI Server will be running.
[Shared Admin] ; Instead of each workstation maintaining its own list of servers, a shared ; tslib.ini file may be placed on a network file system, for example: ;
; tslib.ini=n:\csta\tslib.ini
; This entry overrides the [Telephony Servers] section, if any.
File 1: TSLIB.INI file
File 1: TSLIB.INI file
Section 7: A Practical Example
This chapter examines the steps required to implement a telephony-enabled application. Each step will be
described in detail covering its programming aspect.
The objective of this chapter is to describe the steps required to initialize any TSAPI application (the initialization
steps remain the same for all TSAPI applications). After proper initialization, various operations can be performed
as per the business needs by using the methods provided by TSAPI.
The example given below demonstrates how to create an application which will monitor a device (extension)
to explain TSAPI application initialization procedure. This example is named DeviceMonitor. This example is
developed as a console based application in C++ on the Windows platform using Microsoft Visual Studio.
Each step below describes a particular method/concept with the necessary sample code snippets and the
corresponding UI snapshots. Appendix B: Complete Example Code offers a practical code example which is
developed to demonstrate all TSAPI concepts discussed in this tutorial. The code is documented with full
comments to give reader a better understanding of each API and concept.
Note: All the variables used in this example are initialized with sample values for demonstration purposes; these
values need to be changed appropriately when building and executing the code in your own environment.
7.1 CREATING A NEW PROJECT
Using Microsoft Visual Studio, create a new Win32 Console Application project called “DeviceMonitor”. Follow the
steps below to add the TSAPI Client library and header files for using the TSAPI Client library in the application.
avaya.com
47
7.2 ADDING THE TSAPI CLIENT LIBRARY
TSAPI SDK includes two TSAPI Client libraries, namely csta32.dll and ATTPRV32.dll for the Windows operating
system. csta32.dll contains the TSAPI functions (CSTAServiceName) and the ACSService name services. ATTPRV32.
dll contains Private Data encoding and functions (ATTServiceName).
The corresponding library files for the DLLs need to be added into the project’s linker settings as additional
dependencies. These library files (csta32.lib and ATTPRV32.lib) are generally found in C:\Program Files\Avaya\AE
Services\SDKs\TSAPI\Libs (assuming the TSAPI SDK installation drive is C:\).
Note: A linking error will be thrown if the location where these libraries are stored is not correctly mapped with this
application. For information about installed files, see Reference [2], Appendix A.
7.3 ADDING THE INCLUDE DIRECTIVES
There are some header files which have to be included in the application. Their names and brief descriptions are
given below:
a) acs.h: This file contains event structures, macro definitions and ACS methods. This file needs to be included in
all TSAPI based applications always.
b) csta.h: This file defines CSTA events and methods. This file needs to be included if any CSTA service(s) are
requested.
c) attpriv.h: This file contains definitions for Private Data. Only applications which request for Private Data need to
include this file.
Since the demo application is going to use CSTA service (monitoring a device) as well as request Private Data, it is
required to include all the three header files.
#include "acs.h" // Contains event structure and macros definition #include "csta.h" // CSTA Events Defines #include "attpriv.h" // ATT Private Defines
Note: These header files are part of Avaya TSAPI SDK and will be stored in the SDK installation directory.
Generally, this path is C:\Program Files\Avaya\AE Services\SDKs\TSAPI\Hdrs. Add the path to these files to the
additional include directories settings for the project.
At this point, while building the application, it should compile without any error. The reader can now enter all the
code from Appendix B: Complete Example Code into the DeviceMonitor.cpp file created while creating the project,
and compile the executable.
Following sections describe the sample application logic flow. The code snippets shown earlier and referenced
herein contain code for explanatory purposes and are limited to describing a particular functionality.
7.4 REQUESTING TO OPEN A STREAM
To open a stream, use the acsOpenStream() method provided by API Control Service.
avaya.com
48
If the application does not want to use the extended features of Avaya Communication Manager, the application
can open a stream without Private Data. In this case the application should pass NULL in the Private Data
parameter. (For more information on Private Data, see Chapter 5:).
A TSAPI application can open a stream with Private Data, for which the application needs to negotiate Private Data
Version (PDV) while calling the acsOpenStream() method.
In Appendix B: Complete Example Code, a separate method OpenACSStream() is added that contains the code for
invoking the acsOpenStream() method with Private Data negotiation. Please refer the OpenACSStream() method to
see the complete procedure for invoking the acsOpenStream() method.
7.5 RECEIVING AND HANDLING EVENTS
Once the acsOpenStream() method returns successfully, the application needs to check ACSOpenStreamConfEvent
event to determine whether the stream opened successfully or not. Only after the confirmation event is received,
further requests for any service on the opened stream can be made.
Once the acsOpenStream() method returns successfully, use the acsSetESR() method to set up an event handler
which will be called whenever there is an event in the TSAPI Client library event queue. Code Snippet 11
demonstrates how to use the acsSetESR() method. Refer to the OpenACSStream() method in Appendix B: Complete
Example Code for the complete procedure of invoking acsSetESR() method.
After setting the event handler, the callback routine i.e., the ESRCallback() method, will be invoked whenever the
TSAPI Client library receives any event from TSAPI Service. Getting the ESRCallback method called only means
there is an event in the event queue; the event still remains in the queue and it is application’s responsibility to
retrieve the event from the queue (using either the acsGetEventBlock() or acsGetEventPoll() method). Refer to the
Notify() method in Appendix B: Complete Example Code for the procedure of calling the acsGetEventBlock() and
acsGetEventPoll() methods and parameters definition.
7.6 REQUESTING TO START A MONITOR
CSTA Service requests can be sent on the opened ACS stream as soon as the ACSOpenStreamConfEvent event is received.
The following section describes how to request a CSTA Service from TSAPI service. Monitoring a device service is
used for simplicity. However, the same concept applies to request for other services.
The cstaMonitorDevice() method is used to monitor a device (station extension). Code Snippet 14 demonstrates
the code to monitor a device. When the cstaMonitorDevice() method is called, TSAPI Service sends a confirmation
event CSTAMonitorConfEvent event to confirm that the monitor is added successfully on the device. The code to
handle this event is similar to ACSOpenStreamConfEvent.
When an event, for example CSTAMonitorConfEvent event is retrieved from the event queue, information like Private
Data and MonitorCrossRefID can be extracted from the event object. Refer to the Notify() method in Appendix B:
Complete Example Code for the complete procedure of retrieving event information.
avaya.com
49
7.7 REQUESTING TO STOP A MONITOR
To stop monitoring the monitored device, the cstaMonitorStop() method can be used. The StopMonitor() method
in Appendix B: Complete Example Code demonstrates the use of cstaMonitorStop() method. A confirmation event
will be received for a stop monitor request. Please refer the Notify() method in the sample code to see how an
application can handle CSTA_MONITOR_STOP_CONF confirmation event.
7.8 ERROR HANDLING
While working with TSAPI Client library and TSAPI Service, every TSAPI application should be designed to handle any
CSTA or ACS related error. The application is provided an event to report an error as and when the error is occurred.
The ACSUniversalFailureEvent event can occur at any time (unsolicited) and can indicate, among other things, a
failure or loss of the ACS stream with TSAPI Service.
The ACSUniversalFailureConfEvent event can occur at any time in place of a confirmation event for any of the ACS
functions which have their own confirmation event and indicate a problem in the processes of the requested method.
The most common CSTA errors are reported through CSTAUniversalFailureConfEvent, which represents a negative
acknowledgment for any CSTA service.
Please refer the Notify() method in the sample code to see an example of handling ACS_UNIVERSAL_FAILURE_CONF
and CSTA_UNIVERSAL_FAILURE_CONF events.
7.9 REQUESTING TO CLOSE/ABORT A STREAM
The stream should be closed or aborted once the application finishes its operation on the stream opened with the
TSAPI Service or in case of any critical error. The acsCloseSream() or acsAbortStream() method can be used to close
or abort the stream.
Code Snippet 7 and Code Snippet 9 demonstrate the use of acsCloseStream() and acsAbortStream() methods, respectively.
avaya.com
50
7.10 APPLICATION OUTPUT
When successfully compiled, installed and executed, the DeviceMonitor sample application will display output
similar to the following:
Screenshot 1: Sample code’s output
avaya.com
51
Appendix A: Notes
Note 1. What is InvokeID?
Each service request passes an invokeID to distinguish it from other service requests. invokeID provides a way to
match the confirmation event/failure event to the corresponding request. The application should specify a unique
invokeID so that it will be easy to distinguish between the confirmations events.
There are two alternatives provided by the APIs to generate and associate invokeID with the requests. The application
needs to select one of the following invokeID types and set it as invokeIDType in the acsOpenStream request.
1. Application generated invokeID (APP_GEN_ID): When invokeIDtype is set as APP_GEN_ID, the application takes
the responsibility of generating and providing a unique invokeID internally for any service request. Although
taking the responsibility of providing a unique invokeID puts a burden on the application when service requests
correspond to entries in a data structure, it may simplify application design to use indexes into the data
structure as invokeIDs.
2. TSAPI library generated invokeID (LIB_GEN_ID): When invokeIDtype is set as LIB_GEN_ID, the TSAPI Client library
takes care of providing a unique invokeID for any service request made to the AE Services server. Along with
LIB_GEN_ID, if the application supplies an invokeID in the service request, it will be ignored by TSAPI Service.
Library-generated invokeIDs are unique and simplifies the application design.
Refer the Notify() method in the sample code to see how invokeID can be used to match the confirmation event/
failure event to the corresponding request.
Note 2. Common Return Values
For some APIs, their return value depends on the invokeIDtype parameter value passed in the acsOpenStream() method:
• LIB_GEN_ID - If the application passes LIB_GEN_ID for invokeIDtype, when the method completes successfully, the
method will return a positive value, i.e., the invoke identifier. If the method fails, a negative value (<0) will be
returned. For library-generated invoke identifiers, the return value will never be zero (0).
• APP_GEN_ID - If the application passes APP_GEN_ID for invokeIDtype, when the method completes successfully,
the method will return a zero (0) value. If the method fails, a negative value (<0) will be returned. For
application-generated invoke identifiers, the return value will never be positive (>0).
Note 3. Queue Size
The TSAPI Client library maintains a queue for the requests to be sent and a queue for the received messages
or events. The application can decide the maximum size for these queues while opening the Session. If
the application supplies a zero (0) value, then the default queue size will be used. Parameters sendQSize,
sendExtraBufs, recvQSize and recvExtraBufs are used for this purpose. In normal circumstances, the developer need
not increase the size of the queue. However, if the application requires an increase in the size of the queue, it can
be increased using the parameters described above.
When the queue is full, the TSAPI Client library returns the ACSERROR_QUEUE_FULL error. There could be multiple reasons
for this error and it depends on the application; some of the reasons along with the resolution are given below:
avaya.com
52
• The queue will get full in case client library is receiving request with much higher pace than it can actually
process. In such a case, requesting a bigger sized queue with the help of the sendQSize or recvQSize parameter
in acsOpenStream or redesigning the application to manage the request flow can solve this problem.
• The client library can also throw this error because it is unable to dispatch a message to TSAPI Service because
of a potential network issue that is causing a delay in the message flow. The application developer should try to
check the network connection and throughput to verify that it does not have any problem.
Under normal conditions, the default queue size would be adequate. However, if required, the application can
request the Client library to reserve a queue for a specified number of messages using the sendQSize or recvQSize
parameter in the acsOpenStream() method.
It is suggested that the application should be designed such that upon receiving this error, it notifies all the other
modules to stop sending further requests because the client library is full and so it will consume unnecessary
network bandwidth.
Note 4. Accessing a Secured TSAPI Link (Tlink)
The AE Services server administrator can encrypt the client connections for a TSAPI link that the client
applications wish to connect with. See Reference [3], Chapter 2, “Administering TSAPI Links in AE Services OAM”
for more information.
The client application will receive an ACSERR_NOSERVER error if it is trying to access a secured Tlink without
making sure that the following points hold true:
a) The protocol should be mentioned as “CSTA-S” instead of “CSTA” in the used Tlink. For example, assume
that the client application is using a Tlink named “AVAYA#SWITCH#CSTA#SERVERNAME” with the Tlink set
to ‘unencrypted’. If the Tlink is now set to ‘encrypted’, the client application needs to change the Tlink to
”AVAYA#SWITCH#CSTA-S#SERVERNAME”.
b) The TSAPI Client library version must be 4.1.0 or later.
Note 5. Private Data Version Negotiation
To negotiate Private Data Version (PDV) with the AE Services server, the acsOpenStream() method must be called with the
privateData parameter which is of type ATTPrivateData_t structure. The application specifies the acceptable version(s) in the
data field of the ATTPrivateData_t structure. The data field contains a constant PRIVATE_DATA_ENCODING followed by the null
terminated ACSII string containing the version or range of versions. The version string should be passed as formatted. The
AE Services TSAPI SDK provides the attMakeVersionString() method to simplify formatting of the requested version string.
The application can either request for a specific Private Data Version (PDV) (for e.g. “8”) or provide a range of PDV
(for e.g. “3-8”, “5-7”, etc.) during opening of ACS stream. Currently 8 is the highest PDV supported by the AE
Services server. Requested PDV value indicates that the application is compatible with the specified PDV(s).
The requested version is validated at two levels. First the attMakeVersionString() method applies a preliminary
check on the requested version by comparing the requested version with the version that is currently supported by
avaya.com
53
the Client library. The attMakeVersionString() method returns a value that is greater than 0 if it finds the requested
version is in the currently supported PDV range (by the Client library). For example, if the application requests
range “3-8”, and the Client library only supports version till 5, then this method returns a value that is greater
than 0. The attMakeVersionString() method returns 0 that indicates that the requested version is out of supported
range (i.e less than 1 or greater than the currently supported version).
For example, when the Client library supports version up to and including version 5, and the application has requested:
• version “6-8” or
• version 8 (or any single digit value > 5) or
• “10-25”,
then 0 will be returned to the application.
Second validation happens at the AE Services server level. On receiving requested version with open stream
request, the AE Services server validates the requested version by comparing the requested version with the
version that is currently supported by the AE Services server and returns the supported PDV to the application in
ACSOpenStreamConfEvent confirmation event.
When a range of PDV(s) is provided i.e., “3-8”, the AE Services server picks the highest supported version in the
range specified. If the AE Services server does not support the highest version requested then it picks up the next
highest version it supports and returns that to the application in the ACSOpenStreamConfEvent confirmation event.
For example, if the application requests range “3-8”, and the AE Services server only supports version till 5, then
5 will be returned to the application.
When a specific PDV is requested, and AE Services server supports requested version, then the same requested
version will be returned in the ACSOpenStreamConfEvent confirmation event. For example, if the application requests
version “4”, and the AE Services server supports version till 5, then 4 will be returned to the application.
In any of the following conditions,
• The AE Services server does not support any of the versions from the requested range;
• The requested specific version is out of supported range (i.e less than 1 or greater than the currently supported version);
• NULL is passed in the privateData parameter in The length parameter of ATTPrivateData_t structure is set as 0;
a call to the acsOpenStream() method will still be successful, however, in the ACSOpenStreamConfEvent event,
the length of the Private Data buffer will be 0, indicating that the Private Data negotiation is unsuccessful, i.e.
the application will not be able to access any of the private data extended features, such as single step transfer
call, single step conference call, etc. Also, the application will not receive any Private Data information in the
confirmation/unsolicited events from the AE Services server.
When the PDV is correctly negotiated then the application should only request for the features supported by the
negotiated PDV. Request for any feature(s) not supported by the negotiated PDV will result in failure confirmation event.
avaya.com
54
Appendix B: Complete Example Code
To simplify code understanding, the complete example code is provided here. This source code is also available as
a plain text file from the Avaya DevConnect Portal to simplify inclusion into a sample project file.
// DeviceMonitor.cpp : DeviceMonitor is a console based windows application designed // to demonstrate use of some of the common Telephony Services API's functionality// and basic operation. This application demonstrate common initialization process // required for every TSAPI Client application. After initialization this application // demonstrate Monitoring Services functionality by setting a monitor on// a device (Extension). Other appilcations can be designed to use other services// provided by TSAPI. //// Note: Please ensure library files (CSTA32.lib & ATTPRIV.lib)// are included into Linker's "include" option in project setting. // Following are the major TSAPI APIs demonstrated in this application: // 1: Acquire a list of advertised service name using acsEnumServerNames() method. // 2: Open a API Control Services (ACS) stream using acsOpenStream() method. // 3: Set a method to receive event notification from TSAPI Service// using acsSetESR() method. // 4: Retrieve events from Client library's event queue using acsGetEventBlock() // and acsGetEventPoll() method. // 5: Monitor a device (Extension) using cstaMonitorDevice() method. // 6: Stop monitoring monitored device using cstaMonitorStop() method. // 7: Abort the opened ACS stream using acsAbortStream() method. // 8: Close the opened ACS stream using acsCloseStream() method.
#include "stdafx.h"#include <iostream>#include <conio.h>#include <tchar.h>#include <map>using namespace std;
// TSAPI required header files
// Contains event structure and macros definition #include "acs.h"// CSTA Events Defines #include "csta.h" // ATT Private Defines #include "attpriv.h"
// Gloabal variables declaration - Starts Here
// This demo application stores monitor cross reference ID // and InvokeIDs returned from APIs in global variables for // simplicity, other applications should be designed to store // these values in a map or any other data structure.
// Store the Monitor Cross reference ID returned by the Monitor Service CSTAMonitorCrossRefID_t g_lMonitorCrossRefID;
// To store InvokeID for open stream request int g_nOpenStreamInvokeID = 0; // To store InvokeID for close stream request int g_nCloseStreamInvokeID = 0; // To store InvokeID for start monitor device request int g_nStartMonitorInvokeID = 0; // To store InvokeID for stop monitor device request int g_nStopMonitorInvokeID = 0;
// Handle object used to wait for acsOpenStreamConf Event HANDLE g_hOpenStreamConfEvent; // Handle object used to wait for acsCloseStreamConf Event
avaya.com
55
HANDLE g_hCloseStreamConfEvent;// Handle object used to wait for MonitorDeviceConf Event HANDLE g_hMonitorDeviceConfEvent; // Handle object used to wait for MonitorStopConf Event HANDLE g_hMonitorStopConfEvent;
// Contain private data version that is requested by user // for negotiation.Version_t g_szPrivateDataVersion;
// Count for the available service names static int nServicesCount = 0; // Data structure to store service name map<int, char*> serviceNameMap; typedef pair <int, char*> serviceNamePair;
// Gloabal variables declaration - Ends Here
// Method Declaration - Starts Here
// Method to open an ACS stream bool OpenACSStream(ACSHandle_t* a_pAcsHandle);
// Method to monitor a device void MonitorDevice(ACSHandle_t* a_pAcsHandle);
// Method to stop monitoring void StopMonitor(ACSHandle_t* a_pAcsHandle);
// Method to Close an ACS stream void CloseStream(ACSHandle_t* a_pAcsHandle);
// Method to Abort an ACS Stream void AbortStream(ACSHandle_t* a_pAcsHandle);
// Callback function that will be called when an event // is available in Client library event queue void __stdcall ESRCallback (unsigned long esrParam);
// Method to that retrieve the events from Client library event queue // and process each event. void Notify(ACSHandle_t* a_pAcsHandle);
// Initializes application variables bool InitApplication();
// Print error messages and exit the application void PrintErrorAndExit(ACSHandle_t* a_pAcsHandle);
// Enumerate service names registered with TSAPI Service. void EnumerateServiceNames();
// Method Declaration - Ends Here
// This is starting point where the application will begin from int _tmain(int argc, _TCHAR* argv[]) {
// Maximum wait time interval for receiving an event const int APP_DEF_WAIT_TIMEOUT = 5000; // Five seconds
// For variable initialization defined at application scope InitApplication();
cout<<"************************************************************"<<endl;
avaya.com
56
cout<<" TSAPI SAMPLE CLIENT "<<endl; cout<<"************************************************************"<<endl <<endl;
cout << " Listing all advertised service names below:" << endl; EnumerateServiceNames();
// Open an ACS stream to access features supported by a service // that is advertised by AE Services server.
cout << endl << endl << " Trying to Open an ACS Stream...."<<endl<<endl;
// Store handle of the ACS stream ACSHandle_t* pAcsHandle = new ACSHandle_t;
if ( OpenACSStream(pAcsHandle) ) {
// Wait until we receive a confirmation event for// acsOpenStream() request
if(WaitForSingleObject(g_hOpenStreamConfEvent, APP_DEF_WAIT_TIMEOUT) == WAIT_OBJECT_0) {
// Start monitoring a device cout<<" Trying to Monitor a device..." << endl;
MonitorDevice(pAcsHandle);
// Wait until we receive a confirmation event for// cstaMonitorConf() request if(WaitForSingleObject(g_hMonitorDeviceConfEvent, APP_DEF_WAIT_TIMEOUT)
== WAIT_OBJECT_0) {
do {
// This loop run until user press X or x on console, till that// time we will continue receiving monitor event.
cout << endl << " Monitoring is on, Do you want to stop monitoring now?" << endl <<" Please press x or X to stop the Monitor or any other"
" key to continue monitoring..."<<endl;char chInputChar = _getche(); if(chInputChar == 'X' || chInputChar == 'x')
{ break;
} else
{ // Continue with loop
} }while(true);// loop ends when user press X or x
// Stop the Monitor request cout<<endl<<" Trying to Stop the Monitor...."<<endl<<endl; StopMonitor(pAcsHandle);
// Wait until we receive a confirmation event for// cstaMonitorStopConf() request
if(WaitForSingleObject(g_hMonitorStopConfEvent, APP_DEF_WAIT_TIMEOUT) == WAIT_OBJECT_0) { cout << " Application has received monitor closed"
" confirmation event." << endl;
avaya.com
57
}// end of if else
{ cout << " Error: MonitorStopConfEvent event not received"
" in set time limit." << endl; PrintErrorAndExit(pAcsHandle); } }// end of if
else { cout << " Error: MonitorDeviceConfEvent event not received"
" in set time limit." << endl; PrintErrorAndExit(pAcsHandle); }
}// end of ifelse
{ cout << " Error: OpenStreamConfEvent event not received"
" in set time limit." << endl; PrintErrorAndExit(pAcsHandle); }
// Close the ACS stream
cout<<" Trying to close the stream...."<<endl<<endl;
// Close the opened stream CloseStream(pAcsHandle);
if(WaitForSingleObject(g_hCloseStreamConfEvent, APP_DEF_WAIT_TIMEOUT) == WAIT_OBJECT_0) { cout << " Application has received close"
" stream confirmation event." << endl; }// end of if
else { cout << " Error: CloseStreamConfEvent event not received"
" in set time limit." << endl; PrintErrorAndExit(pAcsHandle); } }
else {
// when the application reaches here then that means// the application is not able to send request for Opening the Stream
cout << " Error: Failed to open stream.... "; }
delete pAcsHandle; return 0;
}// end of main void PrintErrorAndExit(ACSHandle_t* a_pAcsHandle) { cout << " Due to the error occured, the application will close now,"
" please retry after some time." << endl;
// As we are closing the application in an error condition // we should abort the ACS stream using acsAbortStream method // which will free the resource immediately.
cout<<" Trying to abort the stream...."<<endl<<endl;
// Abort the opened stream
avaya.com
58
AbortStream(a_pAcsHandle);
exit(-1); }bool InitApplication() {
// The CreateEvent function creates or opens a named or unnamed// event object.
g_hOpenStreamConfEvent = CreateEvent(0,TRUE,FALSE,0); g_hCloseStreamConfEvent = CreateEvent(0,TRUE,FALSE,0); g_hMonitorDeviceConfEvent = CreateEvent(0,TRUE,FALSE,0); g_hMonitorStopConfEvent = CreateEvent(0,TRUE,FALSE,0);
return true;}
// This is the call back method that will be called when// an incoming event is available in the Client library queue. // @param esrParam: acsHandle passed in this parameter. void __stdcall ESRCallback (unsigned long esrParam) { ACSHandle_t* acsHandle = (ACSHandle_t*) esrParam;
// As the event is now available in the Client library's queue, // other applications should be designed cautiously to retrieve // the event and process it without blocking this thread for a // long time. Writing event processing logic (specially when the// logic is lengthy) in a seperate thread is recommended.
// In this example as the event processing logic is simple and not // lengthy, we are calling method directly from this thread only.
Notify(acsHandle); }
// OpenACSStream() method: This method demonstrates the use of acsOpenStream() // method which is used to establish a communication channel between the// application and the TSAPI Service to access the advertised services. bool OpenACSStream(ACSHandle_t* a_pAcsHandle) {
// constants will be used in acsOpenStream method. const int SEND_QUEUE_SIZE = 0; const int RECEIVE_QUEUE_SIZE = 0; const int SEND_EXTRA_BUF_SIZE = 5; const int RECEIVE_EXTRA_BUF_SIZE = 5; const int MINIMUM_LENGTH = 3;
// Store the Return code of the method RetCode_t nRetCode;
// To hold the advertised service name ServerID_t szServiceName;
// To hold CTI user login ID LoginID_t szLoginID;
// To hold CTI user password Passwd_t szPassword;
// To hold authentication information ACSAuthInfo_t authInfo;
// This demo application request user to enter advertised service Name, // login ID and password. Other application can read this information // from configuration file or any other data source as appropriate.
// Request application user to provide advertised service name cout << " Please select a number corresponding to the advertised"
" service name you wish to work with: ";int nChoice;
avaya.com
59
cin >> nChoice; // The key value in the map starts with 1 and maximum key value // will be one more than size of map. if(nChoice > 0 && nChoice < ((int) serviceNameMap.size() + 1))
{ // Getting service name corresponding to the number choosen// by the user from the map.
strcpy_s(szServiceName, serviceNameMap[nChoice]); }
else { cout << endl << " An incorrect choice made," << " using a default service name." << endl;
// Using default Service name in case user specify incorrect string strcpy_s(szServiceName, "AVAYA#CMSIM#CSTA#AESSIM"); }
// Use acsQueryAuthInfo( ) to determine the login and password requirements // when opening an ACS stream to a particular advertised CSTA service.
nRetCode = acsQueryAuthInfo((ServerID_t*) szServiceName, &authInfo); if(nRetCode != ACSPOSITIVE_ACK)
{ cout << " Error: acsQueryAuthInfo method failed."; }
switch(authInfo.authType) {
case needLoginIdAndPasswd: {
// We should request user to enter both login ID// and password.
cout << endl << " Please enter CTI User login ID: ";// To hold login ID
cin >> szLoginID;
cout << endl << " Please enter CTI User password: ";// To hold password
cin >> szPassword;
break; }
case authLoginIdOnly: {
// We should request user to enter only login ID. cout << endl << " Please enter login ID: ";
// To hold login cin >> szLoginID;
break; }
default: {
// Default login ID for this application strcpy_s(szLoginID, "avaya");
// Default password for this application strcpy_s(szPassword, "Avayapassword@123"); } }
// Assuming login ID and Password minimum length is// greater than three char. Using default if user do not enter // correct credentials. if(strlen(szLoginID) < MINIMUM_LENGTH)
{
avaya.com
60
cout << endl << " An incorrect LoginID is supplied" << ", using default LoginID.";
// Default login ID for this application strcpy_s(szLoginID, "avaya"); }
if(strlen(szPassword) < MINIMUM_LENGTH) { cout << endl << " An incorrect Password is supplied" << ", using default Password.";
// Default password for this application strcpy_s(szPassword, "Avayapassword@123"); }
// To hold the Application Name, the system uses the application // name on certain administration and maintenance status displays.
AppName_t szAppName = "DeviceMonitor"; // Can be empty string
// This parameter contains an ASCII string that is formatted with no spaces,// as follows: TSn-n:5 // where: // TS is a fixed constant (use uppercase characters). // n is a number indicating the TSAPI version // - (hyphen) character indicates a range of versions. // : (colon) character indicates a list of versions. // Example // The following example depicts how an application specifies that it can use// TSAPI versions 1 through 3 (1,2, and 3) and version 5. // TS1-3:5
Version_t szApiVersion = "TS1-2";
// Set a unique value in this parameter in case Application generated // invokeID is used. // As in this example we are using Library generated invokeID, this // parameter value is set to zero.
InvokeID_t lInvokeID = 0;
// Below code demonstrate the process to send private data version// negotiation information in the acsOpenStream() method.
// ATT service request private data buffer ATTPrivateData_t privateData;
// Set the vendor field to 'VERSION' strcpy_s(privateData.vendor, "VERSION");
// Set the data field to a one byte discriminator// PRIVATE_DATA_ENCODING followed by an ASCII string identifying// the version of the private data. Setting the first byte to// PRIVATE_DATA_ENCODING
privateData.data[0] = PRIVATE_DATA_ENCODING;
// A special function is used to convert version string into the format// required by the acsOpenStream function.
cout << endl << " Please enter private data version: "; cin >> g_szPrivateDataVersion;
// Setting the formatted PDV version starting from second byte in the data// field. if((attMakeVersionString(g_szPrivateDataVersion, &(privateData.data[1])))
> 0 ) {
// Setting the privateData length, adding 2 extra bytes here, one byte for
avaya.com
61
// element at zero location (i.e. data[0] as it is one byte long) // and one byte for trailing null (as the ASCII string will be// null terminated).
privateData.length = (unsigned short) strlen(&privateData.data[1]) + 2; }
else {
// attMakeVersionString failed due to incorrect version passed. cout << endl << "attMakeVersionString() method failed..."; cout << endl << "If you want to continue, a default Private Data"
" Version string (3-8) will be used." " Press y/Y to continue or any other key to exit the " " application... ";
char cContinue = _getche(); if(cContinue == 'Y' || cContinue == 'y')
{ // If the attMakeVersionString fails, to continue without private data// set the privateData.length to 0. // Note that in this case private data will not be available to the// application. ACSOpenStreamConfEvent event will receive 0 length PD. // privateData.length = 0;
// As this application make use of private data, setting PDV to a// default value "3-8" in this case.
strcpy_s(g_szPrivateDataVersion, "3-8");
// Calling attMakeVersionString again with default PDV if ( (attMakeVersionString(g_szPrivateDataVersion,
&(privateData.data[1]))) > 0 ) { privateData.length =(unsigned short)strlen(&privateData.data[1]) + 2; }
else {
// Probably using TSAPI client library from the older version exit(-1); } }
else {
// Exit as user do not want to continue. exit(-1); } }
bool bIsSuccess = false;while(!bIsSuccess)
{
// Open a stream with Private Data negotiation & with library// generated InvokeID. This method will returns invokeID on success // as LIB_GEN_ID is used. The invokeID will be stored in nRetCode.
// If the application does not wish to receive Private Data, it should // pass 0 in privateData parameter.
nRetCode = acsOpenStream(a_pAcsHandle, LIB_GEN_ID, // Library takes the control for generating InvokeID. lInvokeID, // This param is ignored when the 2nd parameter is LIB_GEN_ID ST_CSTA, // requesting CSTA stream type. &szServiceName, // CTI Link name "AVAYA#SWITCH1#CSTA#SERVERNAME1" &szLoginID, // CTI user login ID &szPassword, // CTI user password
avaya.com
62
&szAppName, // name of the application ACS_LEVEL1, // LIB Version, will be ignored &szApiVersion, // API Version SEND_QUEUE_SIZE,// send queue size using default 0 SEND_EXTRA_BUF_SIZE, // send extra buf size RECEIVE_QUEUE_SIZE, // receive queue size using default 0 RECEIVE_EXTRA_BUF_SIZE, // receive extra bufs (PrivateData_t *)&privateData // buffer for Private Data );
// If nRetCode is returned as positive value then this suggests// that TSAPI Client Library has accepted the method and // has placed the request in the queue for sending the// request to TSAPI Service. // If negative value is returned then TSAPI CLient library // has rejected the request and has provided the error code.
if(nRetCode > 0) // acsOpenStream returned successfully {
// storing invoke ID for future use g_nOpenStreamInvokeID = (int) nRetCode; bIsSuccess = true; // Stop the loop }
else if(nRetCode < 0) // acsOpenStream failed {
// Other applications should do the error handling as per need// like logging error in a log file or event viewer. For some of these// error you can take corrective action and retry e.g. if you receive// ACSERR_APIVERDENIED, you can try with another version. switch(nRetCode)
{ case ACSPOSITIVE_ACK:
{ // The function is successful
bIsSuccess = true; // Stop the loop break;
} case ACSERR_APIVERDENIED:
{ // This return value indicates that the API Version requested is // invalid and not supported by the existing API Client Library.
cout << endl << " Error: acsOpenStream method failed to" " open stream.." << endl;
// Requesting user to enter TSAPI version again. cout << " Error: API Version is incorrect. Trying again." << endl;
bIsSuccess = false; // Continue the loop break;
} case ACSERR_BADPARAMETER:
{ // One or more parameters invalid. // Validate supplied parameter with the help of// TSAPI Exerciser tool. break;
}default:
{ // Some unhandled error occured const int SLEEP_TIME = 3000;
cout << endl << " Error: acsOpenStream method failed to open stream.."; cout << endl << " Error code: " << nRetCode; Sleep(SLEEP_TIME);
return false;
avaya.com
63
} } }
else // case when nRetCode == 0 {
// Not possible as LIB_GEN_ID is used in this example. } }
// acsSetESR(): This method is used to register a callback method with// Client library to receive a notification when an event is available // in the Client library’s event queue. // If the method is successful, the Client library will call ESRCallback // method whenever there is an event is available in its event queue. // @a_pAcsHandle: Handle returned by the acsOpenStream() method // @ESRCallback: Call back routine invoked when there is an incoming event. // @a_pAcsHandle: An open ACS stream handle is passed in this parameter // whick will be passed in ESRCallback method as parameter. // @notifyAll: TRUE indicates that the ESRCallback will be called// for each new event that arrives in Client library's event queue. // A FALSE value will indicate that the ESRCallback will // only be called each time the Client library's event queue becomes // non-empty.
nRetCode = acsSetESR(*a_pAcsHandle, ESRCallback, (unsigned long)a_pAcsHandle, TRUE);
// Verification for the positive response if(nRetCode != ACSPOSITIVE_ACK)
{ cout<<" ERROR: acsSetESR() method return with an error.";
if(nRetCode == ACSERR_BADHDL) { cout<<" ulAcsHandle being used is not a valid handle"<<endl; }
else { cout << " acsSetESR() failed with unknown error. " << endl; cout << " Error code: " << nRetCode; }
} return true;
}
// MonitorDevice() : This method demonstrates the use of cstaMonitorDevice()// method which is used to monitor the device (Extension) and to receive the// events that arrive at the device after monitor request acknowledged. void MonitorDevice(ACSHandle_t* a_pAcsHandle) {
// Set the DeviceID of the Deivce to be monitored DeviceID_t szDeviceID = "40011"; // Default device ID cout << " Please enter device ID of the device to be monitored: \a"; cin >> szDeviceID;
// Store the return code of the method RetCode_t nRetCode = 0;
CSTAMonitorFilter_t filter; // Store the Montor Filter setting
// Pass 0 for a specific filter category or pass NULL for the filter parameter.
// Call filters are supported for station device.
avaya.com
64
filter.call = cfQueued; // Settting filter for Call Queued event.
// The Agent Filter is supported only for ACD Split devices. filter.agent = 0; // We are using a extension device in this example
// The Feature Filter and Maintenance Filter are not supported. filter.feature = 0; filter.maintenance = 0;
// A zero Private Filter means that the application wants to receive // the private events. If Private Filter is non-zero, private events// will be filtered out.
filter.privateFilter = 0;
// cstaMonitorDevice(): Sending a request to monitor a device. // @a_pAcsHandle : Handle returned from the acsOpenStream() method // @szDeviceID : Specifies the DeviceID of the device to be monitored // @filter : Filter to be set for receiving the events. if NULL is passed// then No filter will be set and application will receive event // reports for all events. // @privateData: Not passing private data with this request.
nRetCode = cstaMonitorDevice( *a_pAcsHandle, // ACS Stream handle 0, // Invoke ID is ignored, as it is library generated &szDeviceID, // ID of the device to be monitored &filter, // Filter setting that will apply on monitor NULL // Private data not passed with this request );
if(nRetCode < 0) { cout<<" Failed to monitor device ID: "<< szDeviceID <<endl; cout<<" Error code: " << nRetCode; }
else {
// cstaMonitorDevice returned successfully g_nStartMonitorInvokeID = nRetCode; } }
// StopMonitor() : This method demonstrates the use of cstaMonitorStop() method // which is used to stop the monitor request for a perticular device. // After invoking this method application will not receive any // event for the device on which monitor service was activated. void StopMonitor(ACSHandle_t* a_pAcsHandle) {
// cstaMonitorStop(): Request to stop the monitor on device. // @a_pAcsHandle: Handle returned from the acsOpenStream() method // @invokeID: Setting it to zero as Library generated ID is used in this // example. // @g_lMonitorCrossRefID: Specifies the Monitor cross reference ID received// in the cstaMonitorDeiviceConf event. // @privateData: - Presently this parameter is ignored so please pass NULL // in this parameter for cstaMonitorStop() method
// Store the Return code of the method RetCode_t nRetCode; nRetCode = cstaMonitorStop (*a_pAcsHandle, 0, g_lMonitorCrossRefID, 0);
// Checking for the negative response
avaya.com
65
if ( nRetCode < 0) { cout<<" Failed to stop monitor"<<endl; cout<<" Error Code :"<<nRetCode;
return; }
else {
// Request is successful. g_nStopMonitorInvokeID = nRetCode; }
}// AbortStream() : This method demonstrates the use of acsAbortStream() method. void AbortStream(ACSHandle_t* a_pAcsHandle) {
// acsAbortStream(): Request to abort an ACS stream. No confirmation event // will be provided for this method. // @a_pAcsHandle: Handle returned from the acsOpenStream() method // @privateData: - Presently this parameter is ignored so please pass NULL // in this parameter for acsAbortStream() method
RetCode_t nRetCode = acsAbortStream(*a_pAcsHandle, NULL);
// Checking for the negative response if(nRetCode<0)
{ if(nRetCode==ACSERR_BADHDL)
{ cout<<" The ACS Handle is invalid "<<endl<<endl; }
else { cout<< " acsAbortStream() failed with unknown error. " << endl; cout << " Error code: " << nRetCode; } }
else { cout<<" ACS Stream aborted successfully... "<<endl<<endl; } }
// CloseStream(): This function demonstrates the use of acsCloseStream() method // which is used to close an ACS stream. The application will be unable to // request services from the AE Services server after the success of this method. void CloseStream(ACSHandle_t* a_pAcsHandle) {
// acsCloseStream(): Request to close an ACS stream. A confirmation event // will be provided for this method. // @a_pAcsHandle: Handle returned from the acsOpenStream() method // @invokeID: Setting it to zero as Library generated ID is used in this // example. // @privateData: - Presently this parameter is ignored so please pass NULL // in this parameter for acsCloseStream() method
RetCode_t nRetCode = acsCloseStream(*a_pAcsHandle, 0, NULL);
// Checking for the negative response if ( nRetCode < 0)
{ // Vrifying for the ACS handle if(nRetCode == ACSERR_BADHDL)
{ // This error indicates the ACS handle passed in acsCloseStream method
avaya.com
66
// is invalid. Please check valid handle value passed to acsCloseSream // method. The handle could be invalid because the stream associated// with it, is already closed or the TSAPI Client library could not find// the associated stream. In this case the application will not receive // an ACSCloseStreamConfEvent.
cout << " The ACS Handle is invalid" << endl; }
else { cout<< " acsCloseStream() failed with unknown error. " << endl; cout << " Error code: " << nRetCode; } }
else { cout << " ACS Stream close request sent successfully... " << endl; g_nCloseStreamInvokeID = nRetCode; } }
// Notify() method will be called by ESRCallback method whenever it // receive a event notification. This method retrive event from event// queue using acsGetEventBlock() or acsGetEventPoll() methods. The // event information is extracted from event object and processed as // per this application design. Other application should be designed to// include event processing logic as per their needs. void Notify(ACSHandle_t* a_pAcsHandle) {
// A boolean variable which will be true if event is successfully // retrieved otherwise false. bool isEventRetrived = false;
// This hold the size the application will allocate initially to// the event buffer. Keeping the size of event buffer will reduce // applications memory footprint. If this size is set to// a smaller value than the size of any event (say 64), then// application receive an error (i.e. ACSERR_UBUFSMALL). In this // case application should try to retrieve event with a larger// event buffer. Below code demonstrate the implementation for this // scenario. const int APP_DEF_DEFAULT_BUFFER_SIZE = 512;
// CSTA event buffer size unsigned short usEventBufSize = APP_DEF_DEFAULT_BUFFER_SIZE; // A pointer to CSTAEvent_t structure, a pointer is being used// instead of a non-pointer variable as non-pointer variable will // reserve fixed memory space on stack
CSTAEvent_t *cstaEvent = NULL; // Number of events in the Client library queue // Default value assumed 1, we will receive exact value after // acsGetEventBlock() method return. unsigned short usNumEvents = 1;
// We are running a loop here which will run until we successfully// retrieved the event or count of events in the Client library// queue has become zero and size of event buffer has become zero. while(!isEventRetrived || ( usNumEvents > 0 && usEventBufSize > 0) )
{ // To hold the error cause int nError;
// ATT service request private data buffer that store // private data coming with event.
avaya.com
67
ATTPrivateData_t privateData;
// Setting the private data length same as the size of // data field of ATTPrivateData_t structure.
privateData.length = sizeof(privateData.data);
// For the next iterations need to free the memory // before reallocating new memory. if(NULL != cstaEvent)
{ //Free the buffer memory
free(cstaEvent); }
// Reallocate buffer in case any failure detected in// retrieving event earlier.
cstaEvent = (CSTAEvent_t*) malloc((SIZE_T) usEventBufSize);
// To store method return value RetCode_t nRetCode;
// BLOCKING_MODE can be defined in the project settings > C/C++ > // Preprocessor to use acsGetEventBlock() method. If application // wants to use acsGetEventPoll method instead, exclude BLOCKING_MODE // from project setting.
#ifdef BLOCKING_MODE {
// Receiving the events in Blocking mode:// GetEventBlock() method is used to extract the events in the// Blocking mode. In the Blocking mode, the application's calling thread// will be blocked until there is an event from the ACS stream.
// @a_pAsHandle: Handle returned from the acsOpenStream() method. // @cstaEvent: Pointer to an area in the application address// space large enough to hold event that // will be copied by Client library. // @usEventBufSize: Specifies the size of event buffer. // @privateData: Points to a buffer which will receive any private// data that accompanies this event. If the application does not wish // to receive private data, then privateData should be set to 0. // @usNumEvents: Specifies the count of events still pending in Client// library queue to be retrieved on this ACS stream.
nRetCode = acsGetEventBlock(*a_pAcsHandle, (void *)cstaEvent, &usEventBufSize, (PrivateData_t *)&privateData, &usNumEvents );
} #else { // Receiving events in Non-Blocking mode // In the Non-Blocking mode the oldest outstanding event from ACS // stream (whose handle is passed in method call) will be copied into // the applications data space and control will be returned immedietly // to the application.
// If no events are currently queued for the application, the function // will return control immediately to the application with an error code // indicating that no events were available.
//Parameter description same as acsGetEventBlock.
avaya.com
68
nRetCode = acsGetEventPoll(*a_pAcsHandle, (void *)cstaEvent, &usEventBufSize, (PrivateData_t *)&privateData, &usNumEvents); } #endif
if(nRetCode != ACSPOSITIVE_ACK) {
if(nRetCode == ACSERR_BADHDL) { cout<<" The ACS Handle is invalid"<<endl<<endl; } // end of if
else if (nRetCode == ACSERR_UBUFSMALL) { cout<<" Passed event buffer size is smaller than the size of the"
" next available event for this ACS Stream."<<endl<<endl;
// The usEventBufSize variable has been reset by the TSAPI Client// Library to the size of the next message on the ACS// stream. The application should call acsGetEventBlock( )// again with a larger buffer. The ACS event still present// on the Client Library queue.
continue; }// end of else if
else if (nRetCode == ACSERR_NOMESSAGE) {
// The acsGetEventPoll()method return this value to indicate // there were no events available in the Client library queue.
cout << " No events available at this time."; }
else { cout << " acsGetEventBlock()/acsGetEventPoll() failed with"
" unknown error. " << endl; cout << " Error code: " << nRetCode;
break; } }// end of if
else {
// Setting true as we have successfully retrieved event isEventRetrived = true;
// Checking for Confirmation event for the Open Stream request switch(cstaEvent->eventHeader.eventClass)
{ case ACSCONFIRMATION:
{ switch(cstaEvent->eventHeader.eventType)
{ case ACS_OPEN_STREAM_CONF:
{ if(g_nOpenStreamInvokeID ==
cstaEvent->event.acsConfirmation.invokeID) { cout << endl <<" acsOpenStremConfEvent received - Stream"
" opened successfully."<<endl; cout << " API Version: " << cstaEvent->event.acsConfirmation.u.acsopen.apiVer << endl; cout << " Library Version: " <<
avaya.com
69
cstaEvent->event.acsConfirmation.u.acsopen.libVer << endl << endl;
// verify that Private Data is correctly negotiated.
// 1st check the length of the Private Data received if (privateData.length <= 0)
{ // handle error condtion ( abort the Stream ) // return error
cout << endl << " Private Data length is zero"" in acsOpenStreamConf event. Private data" " is not sent as a part of this event.";
// As this application is making use of private// data features, in this condition it is// appropriate to close the application as the // private data is not negotiated successfully. // This could happen if you send a PDV which is // not in range of PDV that AE Services server // supports e.g. "10-25", "0-5", "0", "10" etc
PrintErrorAndExit(a_pAcsHandle);
// If we do not close application and use PD // feature, the request in which we are using // PD feature will fail. In this example,// cstaMonitorDevice method will fail if // we do not close the application at this moment.
}
// 2nd Check the vendor String if ( strcmp(privateData.vendor,ECS_VENDOR_STRING) != 0 )
{ // hanlde error condtion ( abort the Stream ) // return error
}
// 3rd check the One byte descriminator if ( privateData.data[0] != PRIVATE_DATA_ENCODING )
{ // handle error condtion ( abort the Stream ) // return error
} else
{ // Retrieving the Private Data
cout <<" PrivateData = VENDOR: "<< privateData.vendor << endl;
// Checking private data version, whether // it is same as requested or not. char cPDVReturned = privateData.data[1];
// To hold returned PDV as number int nReturnedPDV = atoi(&cPDVReturned);
if(strchr(g_szPrivateDataVersion, '-') == NULL) {
// Requested version is specific i.e. does not contain '-' int nRequestedPDV = atoi(g_szPrivateDataVersion);
if(nRequestedPDV == nReturnedPDV) { cout <<" Private data version negotiation is "
avaya.com
70
"successful."; cout <<" Negotiated private data version is: " << cPDVReturned << endl; }
else { cout <<" Private data version negotiation is failed.";
// This is an error condition where AE Server does // not support the PDV requested.
} }
else {
// A range of PDV is requested
// Make a copy of requested PDV value, this // variable will also store first part of requested PDV // after call to strtok_s. char* szFirst = _strdup(g_szPrivateDataVersion); // To store second part of requested PDV char* szSecond;
// strtok_s method is used to split string// which is of format "m-n". This method // will copy m in szFirst and n in szSecond.
strtok_s(szFirst, "-", &szSecond);
int nMinVersion = atoi(szFirst); int nMaxVersion = atoi(szSecond);
// compare value of m and n with // PDV returned. if(nReturnedPDV >= nMinVersion
&& nReturnedPDV <= nMaxVersion ) { cout <<" Private data version negotiation is " "successful."; cout <<" Negotiated private data version is: " << cPDVReturned << endl; }
else { cout <<" Private data version negotiation is failed.";
// This is an error condition where AE Server does // not support the PDV requested.
} } }
// Sets event object to signaled state. SetEvent(g_hOpenStreamConfEvent); }
else {
// Confirmation event received for some other open// stream request.
cout << " A confirmation event received for an unknown open" " stream request.";
} break;
} case ACS_CLOSE_STREAM_CONF:
{
avaya.com
71
if(g_nCloseStreamInvokeID == cstaEvent->event.acsConfirmation.invokeID) {
// Checking for confirmation event for the Close// Stream request.
// Sets event object to signaled state. SetEvent(g_hCloseStreamConfEvent); }
else {
// Confirmation event received for some other close// stream request.
cout << " A confirmation event received for an unknown close" " stream request.";
}
break; }
case ACS_UNIVERSAL_FAILURE_CONF: {
// Checking for the Failure of Open Stream request nError = cstaEvent->event.acsConfirmation.u.failureEvent.error; cout << " ACS_UNIVERSAL_FAILURE_CONF event received" << endl;
// Verifying error is for open stream that this // application has openedif(g_nOpenStreamInvokeID ==
cstaEvent->event.acsConfirmation.invokeID) {
// Checking for the password of the loginID switch( nError )
{ case tserverBadPasswordOrLogin:
{ cout<<" CTI login password is incorrect"<<endl;
break; }
case tserverNoUserRecord: { cout<<" No user object was found in the security"
" database for the login specified in the" " ACSOpenStream request."<<endl;
break; }
default: { cout << " ACS_UNIVERSAL_FAILURE_CONF event received"
" with unknown error"; cout<<" Error Code: "<<nError; } } }
else if(g_nStartMonitorInvokeID == cstaEvent->event.cstaConfirmation.invokeID) {
// Checking for the password of the loginID switch( nError )
{case tserverDeviceNotSupported:
{ cout<<" Error: Device not supported."<<endl;
break; }
avaya.com
72
default: { cout << " ACS_UNIVERSAL_FAILURE_CONF event received"
" with unknown error"; cout<<" Error Code: "<<nError; } } }
else { cout << " An ACS_UNIVERSAL_FAILURE_CONF event received"
" for an unknown request." << endl; cout<<" Error Code: "<<nError; }
break; }
default: {
// Other application should add more cases as per need. cout << " ACS Confirmation event received" << " with unknown event type." << endl; cout<<" Event Type: "<< cstaEvent->eventHeader.eventType; } } // End of switch
break; } // End of ACSCONFIRMATION case
case CSTACONFIRMATION: {
switch(cstaEvent->eventHeader.eventType) {
case CSTA_MONITOR_CONF: {
// Matching the invokeID received in this event with invokeId // received from invoked cstaMonitorDevice method. if(g_nStartMonitorInvokeID ==
cstaEvent->event.cstaConfirmation.invokeID) {
// Sets event object to signaled state.
SetEvent(g_hMonitorDeviceConfEvent);
g_lMonitorCrossRefID = cstaEvent->event.cstaConfirmation.u. monitorStart.monitorCrossRefID;
cout << endl << " csatMonitorDeviceConfEvent received - " " Monitoring started..." << endl << endl;
} else
{ // Confirmation event received for some other monitor// device request.
cout << " A confirmation event received for an unknown" " monitor device request.";
}
break; }
case CSTA_UNIVERSAL_FAILURE_CONF: {
// Checking for the Failure of Monitor Request nError = cstaEvent->event.cstaConfirmation.u.universalFailure.error; cout << " CSTA_UNIVERSAL_FAILURE_CONF event received." << endl;
avaya.com
73
// Verifying error is for monitor start or stop request if(g_nStartMonitorInvokeID ==
cstaEvent->event.cstaConfirmation.invokeID || g_nStopMonitorInvokeID == cstaEvent->event.cstaConfirmation.invokeID) {
// Checking the cause of the error received// for the monitor request switch(nError)
{ case invalidCstaDeviceIdentifier :
{ cout<<" Error: Invalid Devie Identifier."<<endl;
break; }
case resourceBusy : { cout<<" Error: Resource is busy."<<endl;
break; }
case genericOperationRejection : { cout<<" Error: GENERIC_OPERATION_REJECTION"<<endl;
break; }
default: { cout << " Error: CSTA_UNIVERSAL_FAILURE_CONF event " << " received with unknown error code." << endl; cout << " Error Code: "<< nError; } }// end of inner switch }
else { cout << " Error: CSTA_UNIVERSAL_FAILURE_CONF event " << " received with unknown error code." << endl; cout << " Error Code: "<< nError; }
break; }// end of case
case CSTA_MONITOR_STOP_CONF: {
// Matching the invokeID received in this event with invokeId // received from invoked cstaMonitorDevice method. if(g_nStopMonitorInvokeID ==
cstaEvent->event.cstaConfirmation.invokeID) {
// Sets event object to signaled state. SetEvent(g_hMonitorStopConfEvent); cout<<endl<<" Monitor deactivated successfully... "<<endl; }
else {
// Confirmation event received for some other stop monitor// device request.
cout << " A confirmation event received for an unknown" " stop monitor device request.";
}break;
} default:
{ // Other application should add more cases as per need.
avaya.com
74
cout << " CSTA Confirmation event received" << " with unknown event type." << endl; cout<<" Event Type: "<< cstaEvent->eventHeader.eventType; } }// end of CSTA Confirmation event type switch
break; }// end of CSTACONFIRMATION case
case CSTAUNSOLICITED: {
switch(cstaEvent->eventHeader.eventType) {
case CSTA_SERVICE_INITIATED: { cout << " A Service Initiated event is received. ";
break; }
case CSTA_DELIVERED: {
// Delivered event received
// To store the connection state LocalConnectionState_t connectionState;
connectionState = cstaEvent->event.cstaUnsolicited.u.delivered. localConnectionInfo; CSTAEventCause_t eventCause; // To store the event cause eventCause = cstaEvent->event.cstaUnsolicited.u.delivered.cause;
// check the connection state and cause for the event if( connectionState == csAlerting && eventCause == ecNewCall )
{ cout << " Incoming call detected" << endl;
// Retrieving the information associated with this event long lcallID = cstaEvent->event.cstaUnsolicited.u.delivered.
connection.callID; char* szCallingDeviceID = cstaEvent->event.cstaUnsolicited.u.
delivered.callingDevice.deviceID; char* alertingDevice = cstaEvent->event.cstaUnsolicited.u.
delivered.alertingDevice.deviceID;
cout<<" An incoming Call with CallID "<<lcallID<<" received" <<" from "<<szCallingDeviceID<<" to " <<alertingDevice<< endl;
// check the privateData length if ( privateData.length > 0 )
{ // Event buffer that will contain the decoded private data
// information. ATTEvent_t attEvent;
// Check to ensure that private data is successfully decoded. if(attPrivateData(&privateData, &attEvent)==ACSPOSITIVE_ACK )
{ // check the event type if ( attEvent.eventType == ATT_DELIVERED )
{ // This sample code snippet shows how to // extract UCID information from private data, // other application can choose to extract other private// data information as required.
// extract the Universal Call Identifier (UCID) char* UCID = attEvent.u.deliveredEvent.ucid;
cout << " The UCID is: " << UCID;
avaya.com
75
} // End of if } // End of if
else {
// Decoding Error. cout << " An error occured while decoding"
" private data." << endl; } }// End of outer if
else {
// The event does not contain any private Data. } } // End of if
break; } // End of Case
case CSTA_ESTABLISHED: {
// Extract information included in this event here cout << endl << " A Established event is received. "<< endl;
break; } // End of Case
case CSTA_CONNECTION_CLEARED: {
// Extract information included in this event here cout << endl << " A Connection Cleared event is received. "<< endl;
break; } // End of Case
default: {
// Other application should add more cases as per need. cout << " An event of type CSTAUNSOLICITED is received with"
" unknown event class." << endl; cout<<" Event Type: "<< cstaEvent->eventHeader.eventType; } } // End of switch
break; }// end of CSTACONFIRMATION case
default: { cout << " An event received with unknown event class." << endl; cout<<" Event Class: "<< cstaEvent->eventHeader.eventClass; } }// End of event class switch }// end of else }// end of while loop
if(NULL != cstaEvent) {
//Free the buffer memory free(cstaEvent); } } // end of Notify() method
// This is the callback method that will be called for each advertised// service name. serverName method parameter points to an array// of char which is a null-terminated string representing an// advertised service name or Tlink. // This method returns a boolean value which allow application to // request to stop receiving more service names at any point of time // i.e. if this method returns false, it will not be called further // with other service names. Boolean DisplayServerNames(char* szServiceName, unsigned long lParam) {
avaya.com
76
Boolean bReturnValue = FALSE; if(strcmp(szServiceName, "") != 0)
{ // Service name is printed on console here with a number // assigned to each service name. This application store// service name into map for demonstration. User will be // asked to choose any of the service name later.
cout << endl << ++nServicesCount << ". " << "Service Name: " << szServiceName;
// Making a copy of szServiceName in this application // address space. char *szServiceNameTemp = new char[sizeof(ServerID_t)];
strcpy_s(szServiceNameTemp, sizeof(ServerID_t), szServiceName); // Make an entry in the service name map for later use.
serviceNameMap.insert(serviceNamePair(nServicesCount, szServiceNameTemp));
// Return TRUE so that the callback is called // again with the name of the next advertised // service found.
bReturnValue = TRUE; }
else {
// Method received an empty buffer for service name, // returning false in this scenario will cause stop // receiving any more Service name i.e. this method // will not be called again.
//bReturnValue = FALSE; // Not required as default to FALSE }
return bReturnValue; }// This method uses acsEnumServerNames() TSAPI method to specify// a callback method that will be called for each service name. void EnumerateServiceNames() {
// acsEnumServices(): This TSAPI method is used to enumerate// the names of all servers of a specified stream type. // ST_CSTA: Indicates that CSTA services are to be enumerated. // DisplayServerNames: Call back fuction invoked for each service name.
RetCode_t nRetCode = acsEnumServerNames(ST_CSTA, DisplayServerNames, NULL // user defined variable, value of this
// variable will be passed to the // callback method as lParam parameter.// Passed as NULL here.
);
if ( nRetCode != ACSPOSITIVE_ACK ) {
// some error occurred while sending request. cout<<" Error Code: "<<nRetCode;
// handle error. } }
Code Snippet 23: Sample application code
avaya.com
77
Appendix C: References
1. Avaya MultiVantage® Application Enablement Services TSAPI for Avaya Communication Manager Programmer’s
Reference 02-300544
2. Avaya MultiVantage® Application Enablement Services TSAPI, JTAPI, and CVLAN Client and SDK Installation
Guide 02-300543
3. Avaya MultiVantage® Application Enablement Services Administration and Maintenance Guide, Release 4.1,
02-300357
All documents listed are available for download from the Avaya DevConnect Portal to registered members of the
DevConnect Program. See www.avaya.com/devconnect for information on membership and access to the portal.
Please e-mail any questions or comments pertaining to this tutorial along with the full title name and filename,
located in the lower right corner, directly to the Avaya DevConnect Program at [email protected].
About Avaya
Avaya is a global leader in enterprise communications systems. The company
provides unified communications, contact centers, and related services directly
and through its channel partners to leading businesses and organizations
around the world. Enterprises of all sizes depend on Avaya for state-of-the-art
communications that improve efficiency, collaboration, customer service and
competitiveness. For more information please visit www.avaya.com.
avaya.com
©2009 Avaya Inc. All Rights Reserved. Avaya and the Avaya Logo are trademarks of Avaya Inc. All trademarks identified by ® and ™ are registered trademarks or trademarks, respectively, of Avaya Inc. All other trademarks are the property of their respective owners. The information provided in this tutorial is subject to change without notice. The configurations, technical data, and recommendations provided in this tutorial is believed to be accurate and dependable, but is presented without express or implied warranty. Users are responsible for their application of any products specified in this tutorial.09/09