Home

GEONius.com
29-Feb-2016
E-mail

CortexStream - CORTEX Network Stream

CortexStream objects provide a simplified means of communicating with an IN-SNEC CORTEX Command Ranging & Telemetry unit over TCP/IP network connections. The streams can be used for both VME- and NT-based CORTEX units.

A CORTEX message is composed of a fixed-length message header, a variable-length message body, and a fixed-length message trailer; one of the fields in the header specifies the message length (header, body, and trailer). The CortexStream methods are used to read messages from a stream and to write messages to a stream.

The CortexStream constructor creates an unconnected CORTEX stream. If the application is a server accepting connections from CORTEX clients, Answer() must be called to accept a client's connection request:

    #include  "CortexStream.h"			-- CortexStream class.
    ...
    TcpEndpoint  server ("port") ;
    server.Listen () ;				-- Listen for clients.
    ...
    CortexStream  client ;
    server.Answer (client) ;			-- Answer next client.

If the application is a CORTEX client, Call() is called to establish a network connection with the CORTEX unit:

    #include  "CortexStream.h"			-- CortexStream class.
    ...
    CortexStream  stream ("port[>@host]") ;
    stream.Call () ;				-- Connect to CORTEX.

The Read() method reads the next message (header, body, and trailer) from a CORTEX stream:

    CortexHeader  header ;
    char  *body ;
    ...
    stream.Read (header, &body) ;

Read() fills in the header and returns a dynamically-allocated message body; the "length" field in the header contains the length in bytes of the message body (excluding the header and trailer).

Before writing a message to a CORTEX stream, the application must fill in the header with the length of the message body (excluding header and trailer) and the desired flow ID; Write() is then called to output the message to the stream:

    char  body[100] ;
    ...
    ... fill in the message body ...
    ...
    header.length = sizeof body ;
    header.flowID = 0 ;
    stream.Write (header, body) ;

Write() automatically adds the header and trailer lengths to the length field in the header of the outgoing message.

The CORTEX unit transmits and receives data in XDR format; consequently, the data must be converted to and from the client's host-machine format. Read() and Write() take care of decoding and encoding, respectively, the fields in the caller's header structure. The message body is another matter. The VME-based CORTEX only utilizes integers in its messages, so a blanket conversion of each 32-bit quantity in a message body would probably suffice and could conceivably be done automatically by Read() and Write(). The NT-based CORTEX, however, has some single-precision floating-point fields to which xdr_float(3) must be applied. Therefore, the CortexStream class leaves it to the application to perform the appropriate conversions, although not without a little help.

Class method Decode() converts data received on a CORTEX stream from XDR format to host-machine format by applying an XDR conversion function to the input data. The conversion function can be for a user-defined structure that encompasses the entire message body:

    extern  xdrproc_t  xdr_TelemetryFrame ;
    char  *body ;
    CortexHeader  header ;
    TelemetryFrame  frame ;
    ...
    stream.Read (header, &body) ;
    CortexStream::Decode (body, xdr_TelemetryFrame, 0, header.length, &frame) ;
    ...
    ... converted fields stored in FRAME ...
    ...

or for a primitive data type that is repeated throughout the message body:

    long  numbers[256] ;
    ...
    CortexStream::Decode (body, xdr_long, sizeof (long), header.length, &numbers) ;
    ...
    ... header.length/4 integers are converted and stored in NUMBERS ...
    ...

(If the widths of the host-machine data types are identical to the widths of the corresponding XDR types, the conversion could take place in the same memory buffer.)

Encode() is Decode()'s counterpart for converting data from host-machine format to XDR format before calling Write() to output the message on a CORTEX stream:

    char  body[sizeof (long) * 256] ;
    long  numbers[256] ;
    ...
    ... fill in NUMBERS array ...
    ...
    header.length = CortexStream::Encode (numbers, xdr_long, sizeof (long),
                                          sizeof body, body) ;
    header.flowID = 0 ;
    stream.Write (header, body) ;

The actual frame data in a telemetry message, being a byte sequence, can be used directly as received from the CORTEX unit; politically correct applications might wish to call xdr_opaque(3).

The integrity of CORTEX TC (telecommand) request and instruction messages is guaranteed (more or less!) by a checksum computed and appended to the messages. The CortexStream class expects the application to handle checksum processing. Before calling Write() to send a TC message, the application must encode the message body (excluding the checksum) in XDR format, compute the checksum, and add it to the message body in network byte order. The following code fragment sends an "Execute" instruction to the CORTEX unit:

    char  body[5 * BYTES_PER_XDR_UNIT] ;
    long  checksum, i = 0, numbers[4] ;
    ...
    numbers[i++] = 4 ;				-- "Execute" request code.
    numbers[i++] = 1234 ;			-- # of Execute pulses.
    numbers[i++] = 100000 ;			-- Execute pulse width.
    numbers[i++] = 10000 ;			-- Execute pulse period.

    header.length = CortexStream::Encode (numbers, xdr_long, sizeof (long),
                                          i * BYTES_PER_XDR_UNIT, body) ;
    header.flowID = TC flow ID ;

    numbers[i++] = htonl (CortexStream::Checksum (header, numbers)) ;
    header.length += BYTES_PER_XDR_UNIT ;

    stream.Write (header, body) ;

After calling Read() to input a TC message, the application must compute the checksum for the XDR-encoded message body (excluding the checksum) and compare the checksum to the last integer (converted to host byte order) in the body.

    char  *body ;
    long  numbers[256] ;
    ...
    stream.Read (header, &body) ;

    if (CortexStream::Checksum (header, body) !=
        ntohl (*((uint32_t *) &body[header.length-BYTES_PER_XDR_UNIT]))) {
        ... checksum error ...
    }

    CortexStream::Decode (body, xdr_long, sizeof (long), header.length,
                          &numbers) ;

In event-driven applications (e.g., those based on the X Toolkit or the Dispatcher), the socket connection underlying the CORTEX stream, returned by Fd(), can be monitored for input by your event dispatcher. Because input is buffered, the input callback must repeatedly call Read() while IsReadable() is true.

Output can be buffered by specifying a zero or positive timeout in the call to Write(). Subsequently calling Flush() or Write() with a negative timeout results in all of the buffered output being written to the network connection. If output is buffered in an event-driven application, the stream's socket connection can be registered as an output source with the event dispatcher. When the dispatcher detects that the connection is ready for output, the output callback should call Flush() with a zero or positive timeout in order to output as much as it can of the buffered data. After all of the buffered data has been flushed (PendingOutput() returns false), the output callback should unregister the socket connection so as to keep the event dispatcher from endlessly cycling on an output-ready condition.

Configuration and monitoring messages sent to and received from a CORTEX unit contain a numeric code identifying the CORTEX component or table with which the message is concerned. Class methods PartNumber() and PartName(), respectively, provide a convenient means of converting a component/table name to a numeric code and vice-versa. Identically named components have different numeric codes in the VME- and NT-based CORTEX units, so the type of the CORTEX unit must be passed to these methods.


Public Instance Methods

(In addition to those inherited from BufferedTCP.)

CortexStream() - creates a CORTEX stream by service name.
CortexStream() - creates a CORTEX stream by port number.
~CortexStream() - destroys a CORTEX stream.
Read() - reads the next message from the CORTEX.
Write() - writes a message to the CORTEX.

Public Class Methods

Checksum() - compute the checksum for a CORTEX message.
Decode() - decodes XDR-encoded data.
Encode() - encodes data in XDR format.
PartName() - converts a component code to a component name.
PartNumber() - converts a component name to a component code.
PortName() - converts a port number to a port name.
PortNumber() - converts a port name to a port number.

Source Files

CortexStream.cpp
CortexStream.h

Alex Measday  /  E-mail