Compaq TP Desktop Connector
for ACMS
Client Application Programming Guide


Previous Contents Index

6.4.2 Setting Up Polling

In a nonblocking environment, the desktop client program must initiate a control mechanism to poll for pending ACMS messages. To set up polling, the desktop client program does the following:

The acmsdi_dispatch_message service polls for messages from the gateway and calls the appropriate customer-supplied completion routine or presentation procedure, depending on the type of TP Desktop Connector message received.

Example 6-2 illustrates how to set up polling using a timer event.

Example 6-2 Setting Up Polling Using a Timer Event

/**************************************************************************** 
    FUNCTION:   client_init 
 
    SUMMARY:    Initiates timer mechanism to handle incoming Desktop messages. 
****************************************************************************/ 
 
static void client_init(XtAppContext app_context)(1)
{ 
 
 /* 
 **  Initiate a timer event to call check_for_messages() after a 
 **  specified delay (MESSAGE_CHECK_FREQUENCY). The check_for_messages() 
 **  routine will then call acmsdi_dispatch_message to process any 
 **  incoming Desktop messages (this includes End-Of-Desktop-Service 
 **  messages and presentation procedure invocation messages). 
 */ 
 
 DesktopMessageTimer = XtAppAddTimeOut(app_context, 
                                       MESSAGE_CHECK_FREQUENCY, 
                                       check_for_messages, 
                                       app_context); 
 
} 
 
 
/**************************************************************************** 
    FUNCTION:   check_for_messages 
 
    SUMMARY:    Checks for incoming Desktop messages using the 
                TP Desktop Connector client service, acmsdi_dispatch_message(). 
 
    COMMENTS:   This routine is invoked as a result of a timer event 
                and it is responsible for setting the timer to call 
                itself again. 
****************************************************************************/ 
 
static void check_for_messages(XtAppContext app_context)(2)
{ 
  int status; 
 
  /* 
  ** Dispatch any incoming TP Desktop Connector messages: 
  **  If there is a service completion message, a completion 
  **  routine for that service will be dispatched. If there 
  **  is a start exchange step message, a corresponding 
  **  presentation procedure will be dispatched. If there 
  **  are no pending Desktop messages, acmsdi_dispatch_message() 
  **  will return immediately. 
  */ 
 
  status = acmsdi_dispatch_message();(3)
 
  if (status != ACMSDI_NORMAL) 
    printf("Warning! Error status code (%d) received on ACMSDI_DISPATCH_MESSAGE.\n", 
            status); 
 
  /* 
  ** Reset the timer that calls check_for_messages() 
  */ 
 
 
  DesktopMessageTimer = XtAppAddTimeOut(app_context, 
                                        MESSAGE_CHECK_FREQUENCY, 
                                        check_for_messages, 
                                        app_context); 
 
} 
 

Example 6-2 shows the following steps from the module m_avertz.c.

  1. The function client_init establishes the control mechanism (see ).
  2. The client_init routine initiates a timer-driven callback routine, check_for_messages, which X Windows invokes after a specified period of time.
  3. The acmsdi_dispatch_message routine polls for gateway messages to be passed to the desktop client program.

To notify the desktop client program that a TP Desktop Connector message from the gateway is pending, the acmsdi_dispatch_message service calls a customer-supplied completion routine or a generic presentation procedure.

6.4.3 Establishing Session Context

In a nonblocking environment, saving session context globally serves two purposes. First, it saves the local data for reuse. Second, it allows the desktop client program to deal with message passing between the desktop system and the ACMS system when there are single or multiple sign-ins. Also, saving session context globally gives you a convenient place to store session-related user interface data such as form IDs and icon IDs.

In the AVERTZ desktop client program, a user can sign in to a ACMS system in one window and initiate a task from another window. The AVERTZ desktop client program establishes context and maintains the context as session data across service calls and presentation procedures. Example 6-3 shows the definition of data in the session.h file that keeps track of sign-in context.

Example 6-3 AVERTZ Session Context

extern enum request_type { 
              NO_OUTSTANDING_TASK, 
              TASK_IN_PROGRESS, 
              SEND_CTRL_RESV_LIST, 
              SEND_VR_CONTROL_WKSP, 
              TRANS_LIST_3_LIST_2, 
              TRANS_LIST_5_LIST_6, 
              TRANS_LIST_7_LIST_6, 
              TRANS_LIST_8_LIST_9, 
              TRANS_VRH_RCLIST_VRH_RESV_LIST, 
              TRANS_VR_CTRL_WKSP_LIST_D, 
              TRANS_LIST_E_LIST_F, 
              TRANS_LIST_F_LIST_F, 
              TRANS_LIST_G_LIST_H}; 
   .
   .
   .
typedef struct { 
              enum request_type   request_id; 
              char               *receive_control_text; 
              long               *receive_control_text_count; 
              ACMSDI_FORM_RECORD *receive_record; 
          } exchange_request_type; 
 
typedef struct { 
              ACMSDI_SUBMITTER_ID     *submitter_id; (1)
              int                      session_id; 
              ACMSDI_CALL_ID           *call_id;      (2)
              char                     node[MAX_NODE_LENGTH]; 
              char                     username[MAX_USERNAME_LENGTH]; 
              char                     password[MAX_PASSWORD_LENGTH]; 
              Widget                   session_icon; 
              Widget                   icon_pixmap; 
              Widget                   session_menu_item; 
 
              Widget                   resv_form; 
              Widget                   resv_form_ids[MAX_RESV_FORM_IDS]; 
              Widget                   vehicle_form; 
              Widget                   billing_form; 
              exchange_request_type   *current_exchange_request; (3)
              int                      completion_status;        (4)
              char                     task_status_message[80];  (5)
              List                     message_boxes; 
           } session_type; 

The following context data is required for a session:
(1) submitter ID Returned from the ACMS system at sign-in time
(2) call ID Returned by the acmsdi_call_task service
(3) current exchange request Needed if the program uses the same form for different exchange steps
(4) completion status Updated by the TP Desktop Connector client service when the task completes
(5) task status message Updated by the TP Desktop Connector client service when the task completes

The session_type structure enables the desktop client program to access data related to a sign-in session when a Windows operation occurs. The variables submitter_id, call_id, completion_status, and task_status_message are allocated by the desktop client program and updated by TP Desktop Connector client services. The value of the completion_status is updated just before the TP Desktop Connector client services call the completion routine.

The session context structure can also be a useful place to maintain widget IDs of user interface objects related to a session. In the sample, the session context includes widget IDs for the session icon, the session's menu entry in the select menu, widget IDs for all the forms associated with the session, and all the fields in those forms, as well as all message boxes.

The m_avertz program passes the session context to the acmsdi_call_task service as the call_context parameter. Whenever the TP Desktop Connector client services call a task completion routine or a presentation procedure on behalf of an active ACMS task, the session context is passed to the desktop client program. For example, you can use a session context to determine which form in an application to update with data from an incoming presentation procedure. Example 6-4 shows an example in the m_avertz.c code where the session context is passed.

Example 6-4 Context Passed to Desktop Client Program

   .
   .
   .
    status = acmsdi_call_task( 
                current_session_ptr->submitter_id, 
                NULL, 
                "VR_RESERVE_TASK", 
                AVERTZ_APPLICATION_NAME, 
                NULL, 
                current_session_ptr->task_status_message, 
                0, 
                NULL, 
                current_session_ptr->call_id, 
                &(current_session_ptr->completion_status), 
                SessionTask_Complete, 
                (void *) current_session_ptr);  (1)
   .
   .
   .

The parameter at (1) specifies the session context to be passed. When a presentation procedure later starts as a result of the ACMS task executing, the session context is passed back to the desktop client program, as shown in the m_transceive.c code in Example 6-5.

The desktop client program can use that context to determine which form to deal with. The session context is also useful for determining which presentation procedure is in progress or is ending, and which workspaces are affected (see Section 5.5).

Example 6-5 Call Context Returned with Presentation Procedure

long int acmsdi_transceive(ACMSDI_FORMS_SESSION_ID *session_id, 
   .
   .
   .
                           ACMSDI_CALL_ID *call_id, 
                           void *call_context, 
                           ACMSDI_FORM_RECORD *send_records, 
                           ACMSDI_FORM_RECORD *recv_records ) 
{ 
session_type *session_ptr = (session_type *) call_context; 
   .
   .
   .

The sample m_session.c code in Example 6-6 shows how session context is used to establish context for the user interface when the user selects a session icon.

The session_type structure contains the information about the form to display for that submitter.

Example 6-6 Session Context Handling for the User Interface

extern void select_new_session( 
                session_type  *new_session) 
{ 
  session_type *former_current_session_ptr = current_session_ptr; 
 
  current_session_ptr = new_session; 
 
  /* 
  ** If the session that was selected was the current session anyway, 
  ** then there is no need to make any updates to the UI, so return. 
  */ 
 
  if (current_session_ptr == former_current_session_ptr) 
    return; 
 
 /* 
 ** Hide any forms that are displayed for the former current session 
 */ 
 
  if (former_current_session_ptr != NULL) 
    close_session(former_current_session_ptr); 
 
 /* 
 **  Redraw the icons of the former and new current session 
 */ 
 
  if (former_current_session_ptr != NULL) 
    redraw_session_icon(former_current_session_ptr); 
 
  redraw_session_icon(current_session_ptr); 
 /* 
 ** The current session is designated in the 'Select' menu 
 ** with a shaded diamond. 
 ** 
 ** Remove the shaded diamond from the menu entry of the former 
 ** current session and add the shaded diamond to the menu entry 
 ** of the new current session. 
 */ 
 
 if (former_current_session_ptr != NULL) 
  UncheckSessionInSelectMenu (former_current_session_ptr->session_menu_item); 
 
  CheckSessionInSelectMenu (current_session_ptr->session_menu_item); 
 
 /* 
 **  Determine which menus, menu items must be enabled and disabled 
 */ 
 
  if ((current_session_ptr-> 
         current_exchange_request)-> 
           request_id == NO_OUTSTANDING_TASK) 
  { 
       EnableSessionExit(); 
       EnableRentalMenu(); 
       DisableSearchMenu(); 
  } 
  else 
  { 
       DisableSessionExit(); 
       DisableRentalMenu(); 
       if ((current_session_ptr-> 
         current_exchange_request)->request_id == TRANS_LIST_3_LIST_2) 
 
            EnableSearchMenu(); 
       else 
            DisableSearchMenu(); 
   } 
} 

6.4.4 Writing a Call to Other Nonblocking Services

A call to the nonblocking acmsdi_call_task or acmsdi_sign_out service must follow the rules described for other nonblocking services (see Section 5.4.1). The calling routine specifies the submitter identification returned from the acmsdi_sign_in service. The acmsdi_call_task service returns a call identification and call context that are used in any completion routine (see Compaq TP Desktop Connector for ACMS Client Services Reference Manual), presentation procedure, or acmsdi_complete_pp service call.

6.5 Canceling Tasks

TP Desktop Connector allows client programs, written with nonblocking services, to cancel active tasks running on the gateway node. Being able to cancel active tasks allows you to create applications that provide a CANCEL function for the user. The main advantage of being able to cancel a task is to permit the user to work on other applications, if the response from the gateway is not immediate. For example, if the user starts a transaction on the database, you can display three buttons in the dialog box:

This functionality is available with both the portable API client services (acmsdi_cancel) and the Macintosh client services (DBBreak). See Compaq TP Desktop Connector for ACMS Client Services Reference Manual for a description of these client services.

Note

Because of the limitations in MacTCP and NetWare, you cannot use the cancel function with these transports.

You cannot use a cancel service in exchange steps. If you call a cancel during a presentation procedure, TP Desktop Connector returns the message "ACMSDI_EXCHACTV". If you issue a cancel while another cancel is already in progress, TP Desktop Connector returns the message "ACMSDI_CANCELACTV". The cancel completion routine is guaranteed to be called before the task completion routine.

6.6 Writing Nonblocking Presentation Procedures

Writing a presentation procedure in a nonblocking environment differs from writing presentation procedures in a blocking environment. A nonblocking presentation procedure does the following:

In a nonblocking environment, presentation procedures are generally divided as follows:

Typically, the initial and completion routines are separate, so that data can be obtained from the user. If user action is not required as, for example, in a stub routine, the initial routine can call the acmsdi_complete_pp service, and the completion routine is not necessary.

Example 6-7 shows pseudocode from several modules in the AVERTZ sample desktop client program to indicate the flow of processing a presentation procedure.

Example 6-7 Nonblocking Presentation Procedure Pseudocode

long int acmsdi_transceive(ACMSDI_FORMS_SESSION_ID *session_id, (1)
                       char *send_record_id, 
                       long send_record_count, 
                       char *recv_record_id, 
                       long recv_record_count, 
                       char *recv_ctl_text, 
                       long *recv_ctl_text_count, 
                       char *send_ctl_text, 
                       long send_ctl_text_count, 
                       short timeout, 
                       ACMSDI_CALL_ID *call_id, 
                       void *call_context, 
                       ACMSDI_FORM_RECORD *send_records, 
                       ACMSDI_FORM_RECORD *recv_records ) 
{ 
 
 session_type *session_ptr = (session_type *) call_context;  (2)
   .
   .
   .
    if ((0 == strcmp (send_record_id, "LIST_3")) && 
        (0 == strcmp (recv_record_id, "LIST_2"))) 
   .
   .
   .
        /* 
        **  Save Pointers To Exchange Step's Receive Data 
        **  And Call Presentation Procedure 
        */ 
 
        save_sessions_PP_data_ptrs(  (3)
                session_ptr, 
                recv_ctl_text, 
                recv_ctl_text_count, 
                recv_records); 
 
 
        sts = Trans_List3_List2 (  (4)
                session_ptr, 
                send_ctl_text, /** VR_SENDCTRL_WKSP **/ 
                send_records[0].data_record, /** VR_CONTROL_WKSP **/ 
                send_records[1].data_record, /** VR_SITES_WKSP **/ 
                recv_records[0].data_record, /** VR_SITES_WKSP **/ 
                recv_records[1].data_record, /** VR_RESERVATIONS_WKSP **/ 
                recv_records[2].data_record, /** VR_CUSTOMERS_WKSP **/ 
                recv_records[3].data_record /** VR_CONTROL_WKSP **/ 
              ); 
 
    } 
int Trans_List3_List2 (  (5)
            session_type            *session_ptr,...) 
{ 
   .
   .
   .
   enable_initial_fields(session_ptr->resv_form); 
 
   enable_resv_push_buttons(session_ptr->resv_form); 
 
   return(FORMS_NORMAL);  (6)
} 
   .
   .
   .
    return (ts);  (7)
} 

The code shown in Example 6-7 does the following:

  1. The desktop client program is called at the acmsdi_transceive interface.
    The reserve task in the VR_DA_APPL application triggers an exchange step. Refer to the reserve task code shown in Example 4-4 for places where presentation procedures are called. The acmsdi_transceive generic presentation procedure defined in m_transceive.c is called through the polling mechanism. The workspaces from the ACMS system are passed to the AVERTZ desktop client program.
  2. The procedure establishes the session context by doing a type conversion on the call context value.
    The acmsdi_transceive function parses the workspaces to determine which application-specific presentation procedure to call.
  3. The program saves the addresses of the write and modify arguments.
  4. The generic presentation procedure acmsdi_transceive calls the application-specific presentation procedure Trans_List3_List2.
  5. The Trans_List3_List2 function defined in the m_avertzpp.c module gains control to solicit data from the user.
    The routine creates the dialog box and displays the data passed in the workspaces.
  6. Control returns to the acmsdi_transceive function.
    After the data to display is sent to the dialog box, control is returned to the generic function.
  7. Control returns to X Windows.
    The desktop client program allows event processing for other activities to continue.

At this point, the user can enter data in the dialog box and the desktop client program no longer has control.

To signal that data entry is complete and to pass status back to the gateway, the user clicks on the OK button in the dialog box some time after the desktop client program yields control to X Windows. Example 6-8 shows the processing in the m_resvform.c module when the user either signals completion or cancels the operation.

Example 6-8 Presentation Procedure Completion Pseudocode

extern void ResvFormExchangeComplete (     (1)
                       Widget    widget, 
                int      *client_data, 
                       XtPointer call_data) 
            
{ 
  int    button_pressed = *client_data; 
 
  exchange_request_type *exchange_request = 
        current_session_ptr->current_exchange_request; 
  switch (exchange_request->request_id) {   (2)
 
   .
   .
   .
                case TRANS_LIST_3_LIST_2     :   (3)
                  End_Trans_List3_List2(current_session_ptr, 
                                             button_pressed); 
                         break; 
   .
   .
   .
} 
 
 
void End_Trans_List3_List2(                  (4)
     session_type *session_ptr, 
     int    button_pressed) 
 
{ 
   .
   .
   .
  receive_record    = (session_ptr->current_exchange_request)->receive_record; 
 
  sites_wksp     = (vr_sites_wksp *) (receive_record[0].data_record); 
  reservations_wksp = (vr_reservations_wksp *) (receive_record[1].data_record); 
  customers_wksp    = (vr_customers_wksp *) (receive_record[2].data_record); 
  control_wksp     = (vr_control_wksp *) (receive_record[3].data_record); 
   .
   .
   .
  acmsdi_complete_pp(session_ptr->call_id, FORMS_NORMAL);  (5)
 
  create_session_message( 
           session_ptr, 
           AvertzMainWindow, 
           "Reservation Data Has Been Submitted. \nWait For Return Data...", 
           " ", 
           XmDIALOG_WORKING, 
           NULL, 
           NULL);                              
   .
   .
   .
                                 
}                                 (6)

The code in Example 6-8 does the following:


Previous Next Contents Index