Working With The TipcSrv Class


These two programs show code used to send messages between two RTclient processes through RTserver using subjects. They also illustrate using regular C functions as callback functions. For an example using static member functions of a user defined class as callback functions, see the code example in Working with Connections on page 43.

The source code file for this example is located in this directory:

UNIX:
$RTHOME/examples/cxxipc 
OpenVMS:
RTHOME:[EXAMPLES.CXXIPC] 
Windows:
%RTHOME%\examples\cxxipc 

Common Header Source Code

The code for a common header file is:

// rtclient.hxx -- common header for RTclient examples   
 
#define EXAMPLE_PROJECT "example" 
#define EXAMPLE_SUBJECT "rcv" 

Receiver Side

The receiving RTclient creates its connection to RTserver, and receives and processes messages. The receiver code is:

// rtcltrcv.cxx -- RTclient example receiver 
 
// The receiving RTclient creates its connection to RTserver, and 
// receives and processes messages. 
#include <rtworks/cxxipc.hxx> 
#include "rtclient.hxx" 
 
// ================================================================ 
//..cb_process_numeric_data -- numeric data callback 
void cb_process_numeric_data(T_IPC_CONN conn, 
                             T_IPC_CONN_PROCESS_CB_DATA data, 
                             T_CB_ARG arg) 
{ 
  TutOut("Entering cb_process_numeric_data.\n"); 
 
  TipcMsg msg(data->msg);  
 
  // set current field to first field in message 
  msg.Current(0); 
  if (!msg.Status()) { 
    TutOut("Could not set current field of message.\n"); 
    return; 
  }   
 
  T_STR name; 
  T_REAL8 value; 
 
  while (1) { 
    msg >> name >> value; 
    if (!msg.Status()) { 
      break; 
    } 
    TutOut("%s = %f\n", name, value); 
  } 
 
  // make sure we reached the end of the message 
  if (TutErrNumGet() != T_ERR_MSG_EOM) { 
    TutOut("Did not reach end of message.\n"); 
  } 
} // cb_process_numeric_data   
 
// ================================================================ 
//..cb_default -- default callback 
void cb_default(T_IPC_CONN conn, 
                T_IPC_CONN_DEFAULT_CB_DATA data, 
                T_CB_ARG arg) 
{ 
  TutOut("Entering cb_default.\n"); 
 
  TipcMsg msg(data->msg);  
  TipcMt mt(msg.Type()); 
  if (!mt) { 
    TutOut("Could not get message type from message.\n"); 
    return; 
  } 
   
  T_STR name =  mt.Name(); 
  if (!mt) { 
    TutOut("Could not get name from message type.\n"); 
    return; 
  } 
 
  TutOut("Message type name is %s\n", name); 
 
} // cb_default   
 
// ================================================================ 
//..main -- main program 
int main(int argc, char **argv) 
{ 
  T_OPTION option; 
  TipcMt mt; 
   
  // Set the option Project to partition ourselves. 
  option = TutOptionLookup("project"); 
  if (option == NULL) { 
    TutOut("Could not look up the option named project.\n"); 
    return T_EXIT_FAILURE; 
  } 
  if (!TutOptionSetEnum(option, EXAMPLE_PROJECT)) { 
    TutOut("Could not set the option named project.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  // Allow a command-line argument containing the name of a 
  // SmartSockets startup command file. This file can be used to set 
  // options like Server_Names. 
  if (argc == 2) { 
    if (!TutCommandParseFile(argv[1])) { 
      TutOut("Could not parse startup command file %s.\n", 
argv[1]); 
      return T_EXIT_FAILURE; 
    } 
  } 
 
  else if (argc != 1) { // too many command-line arguments 
    TutOut("Usage: %s [ command_file_name ]\n", argv[0]); 
    return T_EXIT_FAILURE; 
  } 
   
  TutOut("Creating connection to RTserver.\n"); 
  TipcSrv& rcvr = TipcSrv::InstanceCreate(T_IPC_SRV_CONN_FULL); 
  if (!rcvr) { 
    TutOut("Could not create the connection to RTserver.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  // create callbacks to be executed when certain operations occur 
  TutOut("Create callbacks.\n"); 
   
  // process callback for NUMERIC_DATA 
  mt.Lookup(T_MT_NUMERIC_DATA); 
  if (!mt) { 
    TutOut("Could not look up NUMERIC_DATA message type.\n"); 
    return T_EXIT_FAILURE; 
  } 
  if (rcvr.ProcessCbCreate(mt,  
                           cb_process_numeric_data, NULL) == NULL) 
{ 
    TutOut("Could not create NUMERIC_DATA process callback.\n"); 
    return T_EXIT_FAILURE; 
  } 
   
  // default callback 
  if (rcvr.DefaultCbCreate(cb_default, NULL) == NULL) { 
    TutOut("Could not create default callback.\n"); 
    return T_EXIT_FAILURE; 
  } 
   
  TutOut("Start subscribing to standard subjects.\n"); 
  if (!rcvr.StdSubjectSetSubscribe(TRUE, FALSE)) { 
    TutOut("Could not start subscribing to standard subjects.\n"); 
    return T_EXIT_FAILURE; 
  } 
   
  TutOut("Start subscribing to the %s subject.\n", 
EXAMPLE_SUBJECT); 
  if (!rcvr.SubjectSubscribe(EXAMPLE_SUBJECT, TRUE)) { 
    TutOut("Could not start subscribing to the %s subject.\n",  
           EXAMPLE_SUBJECT); 
    return T_EXIT_FAILURE; 
  } 
 
  // If an error occurs, then TipcSrv::MainLoop will restart RTserver 
  // and return FALSE. We can safely continue. 
  for (;;) { 
    if (!rcvr.MainLoop(T_TIMEOUT_FOREVER)) { 
      TutOut("TipcSrv::MainLoop failed with error <%s>.\n", 
             TutErrStrGet()); 
    } 
  } 
   
  // This line should not be reached. 
  TutOut("This line should not be reached!!!\n"); 
  return T_EXIT_FAILURE; 
 
} // main   

Sender Side

This sending RTclient creates its connection and publishes messages to a subject (through RTserver). The sending code is:

// rtcltsnd.cxx -- RTclient example sender 
 
// This sending RTclient creates its connection and sends a data frame 
// of messages to a subject (through RTserver). 
 
#include <rtworks/cxxipc.hxx> 
#include "rtclient.hxx" 
 
// ================================================================ 
//..main -- main program 
int main(int argc, char **argv) 
{ 
  T_OPTION option; 
  TipcMt mt; 
 
   // Set the option Project to partition ourselves. 
  option = TutOptionLookup("project"); 
  if (option == NULL) { 
    TutOut("Could not look up the option named project.\n"); 
    return T_EXIT_FAILURE; 
  } 
  if (!TutOptionSetEnum(option, EXAMPLE_PROJECT)) { 
    TutOut("Could not set the option named project.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  // Log outgoing data messages to a message file. Another 
  // way to set options is to use TutCommandParseStr. 
  TutCommandParseStr("setopt log_out_data log_out.msg"); 
 
  // Allow a command-line argument containing the name of a 
  // SmartSockets startup command file. This file can be used to set 
  // options like Server_Names.   
  if (argc == 2) { 
    if (!TutCommandParseFile(argv[1])) { 
      TutOut("Could not parse startup command file %s.\n", 
             argv[1]); 
      return T_EXIT_FAILURE; 
    } 
  } 
  else if (argc != 1) { // too many command-line arguments 
    TutOut("Usage: %s [ command_file_name ]\n", argv[0]); 
    return T_EXIT_FAILURE; 
  } 
 
  TutOut("Creating connection to RTserver.\n"); 
  TipcSrv& sndr = TipcSrv::InstanceCreate(T_IPC_SRV_CONN_FULL); 
  if (!sndr) { 
    TutOut("Could not create the connection to RTserver.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  TutOut("Send a frame of data to the subject.\n"); 
 
  TutOut("Sending a TIME message.\n"); 
  mt.Lookup(T_MT_TIME); 
  if (!mt) { 
    TutOut("Could not look up TIME message type.\n"); 
    return T_EXIT_FAILURE; 
  } 
  if (!sndr.SrvWrite(EXAMPLE_SUBJECT, mt, TRUE, 
                     T_IPC_FT_REAL8, 1.0, 
                     NULL)) { 
    TutOut("Could not send TIME message.\n"); 
  } 
TutOut("Sending a NUMERIC_DATA message.\n"); 
  mt.Lookup(T_MT_NUMERIC_DATA); 
  if (!mt) { 
    TutOut("Could not look up NUMERIC_DATA message type.\n"); 
    return T_EXIT_FAILURE; 
  } 
  if (!sndr.SrvWrite(EXAMPLE_SUBJECT, mt, TRUE, 
                     T_IPC_FT_STR, "voltage", 
                     T_IPC_FT_REAL8, 33.4534, 
                     T_IPC_FT_STR, "switch_pos", 
                     T_IPC_FT_REAL8, 0.0, 
                     NULL)) { 
    TutOut("Could not send NUMERIC_DATA message.\n"); 
  } 
 
  TutOut("Sending an END_OF_FRAME message.\n"); 
  mt.Lookup(T_MT_END_OF_FRAME); 
  if (!mt) { 
    TutOut("Could not look up END_OF_FRAME message type.\n"); 
    return T_EXIT_FAILURE; 
  } 
  if (!sndr.SrvWrite(EXAMPLE_SUBJECT, mt, TRUE, NULL)) { 
    TutOut("Could not send END_OF_FRAME message.\n"); 
  } 
 
   // Each RTclient automatically creates a connection process 
   // callback for CONTROL messages. Use this to send the command 
   // "quit force" to the receiver's command interface. 
  TutOut("Sending a CONTROL message to stop the receiver(s).\n"); 
  mt.Lookup(T_MT_CONTROL); 
  if (!mt) { 
    TutOut("Could not look up CONTROL message type.\n"); 
    return T_EXIT_FAILURE; 
  } 
  if (!sndr.SrvWrite(EXAMPLE_SUBJECT, mt, TRUE, 
                     T_IPC_FT_STR, "quit force", 
                     NULL)) { 
    TutOut("Could not send CONTROL message.\n"); 
  } 
 
  // Flush the buffered outgoing messages to RTserver. 
  if (!sndr.Flush()) { 
    TutOut("Could not flush outgoing messages to RTserver.\n"); 
  } 
 
  // Completely disconnect from RTserver. 
 
  if (!sndr.Destroy(T_IPC_SRV_CONN_NONE)) { 
    TutOut("Could not fully destroy connection to RTserver.\n"); 
  } 
  TutOut("Sender RTclient process exiting successfully.\n"); 
  return T_EXIT_SUCCESS; // all done 
 
} // main  

Running Receiving and Sending Programs

Compile and link the receiving and sending programs (see Compiling, Linking, and Running on page 13 for generic instructions).

To run the programs, start the receiving process first in one terminal emulator window and then the sending process in another terminal emulator window.

Start RTserver, then start up the receiving program in the first window:

UNIX:
$ rtcltrcv.x  
OpenVMS:
$ run rtcltrcv.exe 
Windows:
$ rtcltrcv.exe 

Start up the sending program in the second window:

UNIX:
$ rtcltsnd.x  
OpenVMS:
$ run rtcltsnd.exe 
Windows:
$ rtcltsnd.exe 

The output from the receiving process is similar to this:

Creating connection to RTserver. 
Connecting to project <example> on <_node> RTserver. 
Using local protocol. 
Message from RTserver: Connection established. 
Start subscribing to subject </_workstation1_7704>. 
Create callbacks. 
Start subscribing to standard subjects. 
Start subscribing to subject </_workstation1>. 
Start subscribing to subject </_all>. 
Start subscribing to the rcv subject. 
Entering cb_default. 
Message type name is time 
Entering cb_process_numeric_data. 
voltage = 33.453400 
switch_pos = 0.000000 
Entering cb_default. 
Message type name is end_of_frame 

The output from the sending process is similar to this:

Now logging outgoing data-related messages to <log_out.msg>. 
Creating connection to RTserver. 
Connecting to project <example> on <_node> RTserver. 
Using local protocol. 
Message from RTserver: Connection established. 
Start subscribing to subject </_workstation1_7706>. 
Send a frame of data to the receiving subject. 
Sending a TIME message. 
Sending a NUMERIC_DATA message. 
Sending an END_OF_FRAME message. 
Sending a CONTROL message to stop the receiver(s). 
Sender RTclient process exiting successfully. 

This is the message file log_out.msg, which is created by the sender:

time /rcv 1 
numeric_data /rcv { 
 voltage 33.4534 
 switch_pos 0 
} 
end_of_frame /rcv 
control /rcv "quit force" 

TIBCO SmartSockets™ cxxipc Class Library
Software Release 6.8, July 2006
Copyright © TIBCO Software Inc. All rights reserved
www.tibco.com