↑ Software ↑

GEONius.com
2-Oct-2022
 E-mail 

ins_util - IN-SNEC CORTEX Utilities

The INS_UTIL package provides a simplified means of communicating with an IN-SNEC CORTEX Command Ranging & Telemetry unit over TCP/IP network connections. Layered on top of the lower-level TCP_UTIL package, the INS_UTIL package can be used for both VME- and NT-based CORTEX units. (The package has been built and tested on Unix, Windows, and VMS platforms.)

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 INS_UTIL functions are used to read messages from a stream and to write messages to a stream.

A CORTEX stream is created on a previously established network connection. As a result, the INS_UTIL package can be used for implementing both client and server CORTEX applications. The following code fragment connects to the logging port on a CORTEX unit:

    #include  <stdio.h>				-- Standard I/O definitions.
    #include  "tcp_util.h"			-- TCP/IP networking utilities.
    #include  "ins_util.h"			-- IN-SNEC CORTEX utilities.

    char  name[64] ;
    CortexStream  stream ;
    TcpEndpoint  connection ;
    ...
    sprintf (name, "%d@host", insPort ("LOG")) ;
    tcpCall (name, 0, &connection) ;		-- Call LOG unit ("3040@host").
    insCreate (connection, &stream) ;		-- Create CORTEX stream.

The insPort() function used above provides a convenient means for determining the CORTEX ports by name; insPortName() is available for performing the reverse translation.

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

    CortexHeader  header ;
    char  *body ;
    ...
    insRead (stream, -1.0, &header, &body) ;

insRead() fills in the header and returns a pointer to the message body (which is stored in memory private to the INS_UTIL package); 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; insWrite() is then called to output the message to the stream:

    #include  <stdlib.h>			-- Standard C Library definitions.
    char  body[100] ;
    ...
    ... fill in the message body ...
    ...
    header.length = sizeof body ;
    header.flowID = 0 ;
    insWrite (stream, -1.0, &header, body) ;

insWrite() automatically adds the header and trailer lengths to the length field in the header of the outgoing message. Both insRead() and insWrite() take a timeout argument that allows the application to limit the amount of time these routines wait to perform their respective functions.

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. insRead() and insWrite() 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 insRead() and insWrite(). The NT-based CORTEX, however, has some single-precision floating-point fields to which xdr_float(3) must be applied. Therefore, the INS_UTIL utilities leave it to the application to perform the appropriate conversions, although not without a little help.

NOTE: The XDR representations of a host CPU's ints and longs are both 32-bits wide. To ensure no loss of precision, applications built for platforms on which C ints are less than 32-bits wide should use longs and xdr_long(3) when encoding and decoding CORTEX messages. (Assuming long is at least 32-bits wide.)

insDecode() 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 ;
    ...
    insRead (stream, -1.0, &header, &body) ;
    insDecode (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] ;
    ...
    insDecode (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.)

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

    char  body[sizeof (long) * 256] ;
    long  numbers[256] ;
    ...
    ... fill in NUMBERS array ...
    ...
    header.length = insEncode (numbers, xdr_long, sizeof (long),
                               sizeof body, body) ;
    header.flowID = 0 ;
    insWrite (stream, -1.0, &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 INS_UTIL package expects the application to handle checksum processing. Before calling insWrite() 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 = insEncode (numbers, xdr_long, sizeof (long),
                               i * BYTES_PER_XDR_UNIT, body) ;
    header.flowID = TC flow ID ;

    checksum = insChecksum (&header, body) ;
    *((uint32_t *) &body[header.length]) = htonl (checksum) ;
    header.length += BYTES_PER_XDR_UNIT ;

    insWrite (stream, -1.0, &header, body) ;

After calling insRead() 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] ;
    ...
    insRead (stream, -1.0, &header, &body) ;

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

    insDecode (body, xdr_long, sizeof (long), header.length, &numbers) ;

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

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. insPart() and insPartName(), 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 insPart().

When a CORTEX stream is no longer needed, a single call will close the network connection and discard the stream:

    insDestroy (stream) ;

Public Procedures

insChecksum() - compute the checksum for a message.
insCreate() - creates a CORTEX network stream.
insDecode() - decodes XDR-encoded data.
insDestroy() - deletes a CORTEX network stream.
insEncode() - encodes data in XDR format.
insFd() - returns a CORTEX stream's socket number.
insIsReadable() - checks if input is waiting to be read from a stream.
insIsUp() - checks if a CORTEX stream is up.
insIsWriteable() - checks if data can be written to a stream.
insName() - returns the name of a CORTEX stream.
insPart() - converts a component name to a component code.
insPartName() - converts a component code to a component name.
insPort() - converts a port name to a port number.
insPortName() - converts a port number to a port name.
insRead() - reads the next message from a CORTEX stream.
insWrite() - writes a message to a CORTEX stream.

Source Files

ins_util.c
ins_util.h

Alex Measday  /  E-mail