Implementation notes

This section provides more detailed information about the implementation of the sample application, and suggestions for developing your own applications which use TDM connection management.

Error handling

Error handling in the sample application is simplified to make the code easy to understand. Errors in the custom server are treated as fatal and the custom server will exit. Errors in the state tables cause a “technical difficulties” announcement to be played to the caller and the call terminates. If you develop a production application based on this sample, you should enhance the error handling to improve reliability and usability.

ASCII state tables

TromboneIn, TromboneOut and TromboneMonitor were written as ASCII state tables. The source code for the state tables is in the custom server directory, $CUR_DIR/ca/TromboneCS_dir. The ASCII state table format makes it easier to understand how the application works. If you want to change the state tables, you may find it easier to change the ASCII versions, and import the modified state tables, rather than making changes using the State Table window.

TromboneCS state machine

Each “trombone”, that is, connection between a calling and a called party, is described by a state machine, so that the trombone moves between various states according to events, which are the custom server user functions. Figure 1 shows the states and events in this state machine.

Figure 1. The TromboneCS state machine
The state machine is shown as a sequence of states connected by events. The sequence is as follows: state t_idle; event t_outdial(); state t_dialing; event t_outdial_complete(); state t_connected; event t_chsl(); state t_disconnected; event t_chsl().

Data structures

TromboneCS uses two main data structures:

A structure which holds the status of one trombone, including the link_id of each of the two channel processes involved, the connection_id returned from CA_TDM_Connect(), the current state of the connection, and a copy of the t_outdial() request which initiated this trombone, and which is modified to become the t_outdial() response when the trombone is connected.
An array of pointers to trombone structures, one for each channel process in the system. This provides a way to access the trombone data structure for a particular channel process, by using the link_id as an index into the array. A null pointer in this array means that the corresponding channel process is not involved in a trombone connection.

Custom server main() function

When you develop a custom server, you can choose to have Blueworx Voice Response create the main() function, or provide your own. TromboneCS provides its own main() function, because the system generated main() assumes that each user function will return a response to the state table when the user function returns. TromboneCS cannot return a response to the state table as the t_outdial() function returns, because it must wait for the t_outdial_complete() message from TromboneOut to discover whether the outdial attempt was successful. The main() function is modified compared to the system-generated main() to take account of this.

TDM connection token

When you make a TDM connection using CA_TDM_Connect(), you have to supply an unsigned integer as a token for that connection. The same token must be supplied when the connection is broken, either by CA_TDM_Disconnect() or by making a new connection involving the same ports. This simple security mechanism prevents unauthorized custom servers from breaking your connections. As a custom server writer, it is your responsibility to choose a token for your connections. The sample application uses a four character string, “TROM”, and casts this into an unsigned integer for use as a token.

Cleaning up TDM connections

TDM connection management is designed to break TDM connections automatically when a caller involved in the connection hangs up. This protects against callers hearing spurious speech when a call ends and the telephony channel is reused. However, resources associated with the connection remain allocated until the connection has been explicitly disconnected by a call to CA_TDM_Disconnect(). So it is your responsibility as a custom server writer to disconnect any TDM connections you establish. A good way is to keep track of connections made by each channel process, and to provide a CloseHostServerLink function. When the custom server is notified via this function that a channel process has detached from the custom server, it can disconnect all TDM connections associated with that channel process. This technique is illustrated by TromboneCS.

Notifying hang-ups to the partner channel process

When one of the parties connected by a trombone hangs up, the TromboneMonitor state table interacting with this party detects the hangup event and exits. But the application must also provide a way for this hangup to be notified to the state table interacting with the other party. There are two steps in this process:

  1. TromboneCS detects that the hung-up party's state table has terminated, as the t_chsl() function is called.
  2. t_chsl() uses the CA_Report_Channel_Event() subroutine to send a channel event to the remaining party's state table. A channel event is an asynchronous notification from custom server to a state table. A channel event interrupts Play... actions and the WaitEvent action.

    TromboneMonitor uses the WaitEvent action to monitor for any one of: DTMF key, caller hangup or channel event. WaitEvent can also be configured to detect voice energy or fax tones if you wish, by setting system variables, as described in the Blueworx Voice Response for AIX: Application Development using State Tables information. Each channel event contains an event type and an information field, which can be used to provide different types of notifications from custom servers to state tables.

    The sample application uses only one type of event, so the event and information fields are set to zero. When TromboneMonitor receives a channel event, it makes a short announcement to the attached party and then terminates the call.

Moving to a multiprocess custom server

The CA_TDM_Connect() and CA_TDM_Disconnect() custom server subroutines will block your custom server process while waiting for a response from TDM connection management. This means that a single-process custom server design, as implemented in the sample, may not be suitable for a production environment where high throughput is required. This issue is discussed in “Designing a multiprocess custom server” in the Blueworx Voice Response for AIX: Custom Servers reference manual.

A multiprocess non-associated design would be a good choice for this application. You could fork() several identical copies of the custom server process to increase throughput, and place t_array, the array of trombone data structures in shared memory to allow access from any of the processes. You should allocate storage for the trombone data structures statically in shared memory, instead of using malloc() as in the sample, because storage allocated by malloc() is not accessible outside the process which allocated it.

Voice paths

The main purpose of the trombone application is to allow the two parties carry out a conversation. However, one of the benefits of using Blueworx Voice Response to provide this service is that your application can also make announcements to the parties, record their voices or allow them to use DTMF keys to invoke features of the service. In general, you can use Play, Record and GetKey actions freely in your state tables, and they will behave as you expect. But it is useful to have a clear understanding of the voice paths in the system so that you understand Blueworx Voice Response's capabilities and limitations.

Figure 2 shows the voice paths which are in effect while a trombone connection is established. State table A can always record the spoken voice of party A, Tom for example, or detect Tom's DTMF keys. Likewise, state table B can always hear party B, Daisy for example.

Figure 2. Voice paths in a trombone connection
The graphic illustrates the preceding text but also shows the TDM bus connecting Tom and Daisy.

State table A cannot hear Daisy's voice or detect Daisy's DTMF keys. Likewise, state table B cannot hear Tom.

Figure 3 and Figure 4 show the switches that control what each party can hear. Your application does not need to do anything to control the position of these switches: they are manipulated automatically by Blueworx Voice Response. Each switch can be in one of two positions:

Echo susceptibility

Voice paths stated that state table A cannot hear Daisy's voice and state table B cannot hear Tom. This rule may not hold true if the circuit connecting Blueworx Voice Response to one of the parties is prone to echo. In this case, state table A may hear Daisy's voice or DTMF echoed via Tom's voice circuit or handset. The echo cancellation feature of the digital trunk adapters cannot be used to reduce these echoes, because the output from the TDM bus by-passes the digital signal processors in the pack.

If your customers use echo-prone telephone connections, you should take this into account in your application design. For example, it may be better to offer DTMF facilities to the calling party but not to the called party. If you disable DTMF detection in one of the state tables, you avoid the risk that a DTMF detector may be triggered by an echoed DTMF tone.