CortexStream - CORTEX Network StreamCortexStream 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.
(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.
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.
CortexStream.cpp
CortexStream.h