Previous | Contents | Index |
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.
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(); } } |
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.
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:
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 |